1 #include <stdio.h> 2 3 #include "api_exch.h" 4 5 static int sock; /* Socket number */ 6 7 static char whoarewe[40] = ""; 8 #define WHO_ARE_WE() fprintf(stderr, "(API %s) ", whoarewe); 9 10 static enum {CONTENTION, SEND, RECEIVE } conversation; 11 12 static struct exch_exch exch_state; 13 14 static unsigned int 15 my_sequence, 16 your_sequence; 17 18 static char ibuffer[40], *ibuf_next, *ibuf_last; 19 #define IBUFADDED(i) ibuf_last += (i) 20 #define IBUFAVAILABLE() (ibuf_last -ibuf_next) 21 #define IBUFFER() ibuffer 22 #define IBUFGETBYTES(w,l) { memcpy(w, ibuf_next, l); ibuf_next += l; } 23 #define IBUFGETCHAR() (*ibuf_next++) 24 #define IBUFGETSHORT() ((*ibuf_next++<<8)|(*ibuf_next++&0xff)) 25 #define IBUFRESET() (ibuf_next = ibuf_last = ibuffer) 26 27 char obuffer[40], *obuf_next; 28 #define OBUFADDBYTES(w,l) { memcpy(obuf_next, w, l); obuf_next += l; } 29 #define OBUFADDCHAR(c) (*obuf_next++ = c) 30 #define OBUFADDSHORT(s) {*obuf_next++ = (s)>>8; *obuf_next++ = s; } 31 #define OBUFAVAILABLE() (obuf_next - obuffer) 32 #define OBUFFER() obuffer 33 #define OBUFRESET() obuf_next = obuffer 34 #define OBUFROOM() (obuffer+sizeof obuffer-obuf_next) 35 36 37 static int 38 outflush() 39 { 40 int length = OBUFAVAILABLE(); 41 42 if (length != 0) { 43 if (write(sock, OBUFFER(), length) != length) { 44 WHO_ARE_WE(); 45 perror("write"); 46 return -1; 47 } 48 OBUFRESET(); 49 } 50 return 0; /* All OK */ 51 } 52 53 54 static int 55 infill(count) 56 int count; 57 { 58 int i; 59 60 if (OBUFAVAILABLE()) { 61 if (outflush() == -1) { 62 return -1; 63 } 64 } 65 if (ibuf_next == ibuf_last) { 66 IBUFRESET(); 67 } 68 if ((count -= IBUFAVAILABLE()) < 0) { 69 return 0; 70 } 71 while (count) { 72 if ((i = read(sock, IBUFFER(), count)) < 0) { 73 WHO_ARE_WE(); 74 perror("read"); 75 return -1; 76 } 77 if (i == 0) { 78 /* Reading past end-of-file */ 79 WHO_ARE_WE(); 80 fprintf(stderr, "End of file read\r\n"); 81 return -1; 82 } 83 count -= i; 84 IBUFADDED(i); 85 } 86 return 0; 87 } 88 89 static char * 90 exch_to_ascii(exch) 91 int exch; /* opcode to decode */ 92 { 93 switch (exch) { 94 case EXCH_EXCH_COMMAND: 95 return "Command"; 96 case EXCH_EXCH_TYPE: 97 return "Type"; 98 case EXCH_EXCH_TURNAROUND: 99 return "Turnaround"; 100 case EXCH_EXCH_RTS: 101 return "Request to Send"; 102 default: 103 { 104 static char unknown[40]; 105 106 sprintf(unknown, "(Unknown exchange 0x%02x)", exch&0xff); 107 return unknown; 108 } 109 } 110 } 111 112 /* 113 * Send the exch structure, updating the sequnce number field. 114 */ 115 116 static int 117 send_state() 118 { 119 if (OBUFROOM() < sizeof exch_state) { 120 if (outflush() == -1) { 121 return -1; 122 } 123 } 124 exch_state.my_sequence = ++my_sequence; 125 exch_state.your_sequence = your_sequence; 126 OBUFADDBYTES((char *)&exch_state, sizeof exch_state); 127 return 0; 128 } 129 130 /* 131 * Receive the exch structure from the other side, checking 132 * sequence numbering. 133 */ 134 135 static int 136 receive_state() 137 { 138 if (IBUFAVAILABLE() < sizeof exch_state) { 139 if (infill(sizeof exch_state) == -1) { 140 return -1; 141 } 142 } 143 IBUFGETBYTES((char *)&exch_state, sizeof exch_state); 144 if (conversation != CONTENTION) { 145 if (exch_state.your_sequence != my_sequence) { 146 WHO_ARE_WE(); 147 fprintf(stderr, "Send sequence number mismatch.\n"); 148 return -1; 149 } 150 if (exch_state.my_sequence != ++your_sequence) { 151 WHO_ARE_WE(); 152 fprintf(stderr, "Receive sequence number mismatch.\n"); 153 return -1; 154 } 155 } else { 156 /* In contention state, no sequence numbering */ 157 your_sequence = exch_state.my_sequence; 158 } 159 return 0; 160 } 161 162 static int 163 enter_receive() 164 { 165 switch (conversation) { 166 case CONTENTION: 167 exch_state.opcode = EXCH_EXCH_TURNAROUND; 168 if (send_state() == -1) { 169 return -1; 170 } 171 if (receive_state() == -1) { 172 return -1; 173 } 174 if (exch_state.opcode != EXCH_EXCH_RTS) { 175 WHO_ARE_WE(); 176 fprintf(stderr, "In CONTENTION state: "); 177 if (exch_state.opcode == EXCH_EXCH_TURNAROUND) { 178 fprintf(stderr, 179 "Both sides tried to enter RECEIVE state.\n"); 180 } else { 181 fprintf(stderr, 182 "Protocol error trying to enter RECEIVE state.\n"); 183 } 184 return -1; 185 } 186 break; 187 case SEND: 188 exch_state.opcode = EXCH_EXCH_TURNAROUND; 189 if (send_state() == -1) { 190 return -1; 191 } 192 break; 193 } 194 conversation = RECEIVE; 195 return 0; 196 } 197 198 static int 199 enter_send() 200 { 201 switch (conversation) { 202 case CONTENTION: 203 exch_state.opcode = EXCH_EXCH_RTS; 204 if (send_state() == -1) { 205 return -1; 206 } 207 /* fall through */ 208 case RECEIVE: 209 if (receive_state() == -1) { 210 return -1; 211 } 212 if (exch_state.opcode != EXCH_EXCH_TURNAROUND) { 213 WHO_ARE_WE(); 214 fprintf(stderr, "Conversation error - both sides in SEND state.\n"); 215 return -1; 216 } 217 } 218 conversation = SEND; 219 return 0; 220 } 221 222 int 223 api_exch_nextcommand() 224 { 225 if (conversation != RECEIVE) { 226 if (enter_receive() == -1) { 227 return -1; 228 } 229 } 230 if (receive_state() == -1) { 231 return -1; 232 } 233 if (exch_state.opcode != EXCH_EXCH_COMMAND) { 234 WHO_ARE_WE(); 235 fprintf(stderr, "Expected a %s exchange, received a %s exchange.\n", 236 exch_to_ascii(EXCH_EXCH_COMMAND), exch_to_ascii(exch_state.opcode)); 237 return -1; 238 } 239 return exch_state.command_or_type; 240 } 241 242 243 int 244 api_exch_incommand(command) 245 int command; 246 { 247 int i; 248 249 if ((i = api_exch_nextcommand()) == -1) { 250 return -1; 251 } 252 if (i != command) { 253 WHO_ARE_WE(); 254 fprintf(stderr, "Expected API command 0x%x, got API command 0x%x.\n", 255 command, i); 256 return -1; 257 } 258 return 0; 259 } 260 261 262 int 263 api_exch_outcommand(command) 264 int command; 265 { 266 if (conversation != SEND) { 267 if (enter_send() == -1) { 268 return -1; 269 } 270 } 271 exch_state.command_or_type = command; 272 exch_state.opcode = EXCH_EXCH_COMMAND; 273 if (send_state() == -1) { 274 return -1; 275 } else { 276 return 0; 277 } 278 } 279 280 281 int 282 api_exch_outtype(type, length, location) 283 int 284 type, 285 length; 286 char 287 *location; 288 { 289 int netleng = htons(length); 290 291 if (conversation != SEND) { 292 if (enter_send() == -1) { 293 return -1; 294 } 295 } 296 exch_state.opcode = EXCH_EXCH_TYPE; 297 exch_state.command_or_type = type; 298 exch_state.length = netleng; 299 if (send_state() == -1) { 300 return -1; 301 } 302 if (length) { 303 if (OBUFROOM() > length) { 304 OBUFADDBYTES(location, length); 305 } else { 306 if (outflush() == -1) { 307 return -1; 308 } 309 if (write(sock, location, length) != length) { 310 WHO_ARE_WE(); 311 perror("write"); 312 return -1; 313 } 314 } 315 } 316 return 0; 317 } 318 319 320 int 321 api_exch_intype(type, length, location) 322 int 323 type, 324 length; 325 char 326 *location; 327 { 328 int i, netleng = htons(length); 329 330 if (conversation != RECEIVE) { 331 if (enter_receive() == -1) { 332 return -1; 333 } 334 } 335 if (receive_state() == -1) { 336 return -1; 337 } 338 if (exch_state.opcode != EXCH_EXCH_TYPE) { 339 WHO_ARE_WE(); 340 fprintf(stderr, 341 "Expected to receive a %s exchange, received a %s exchange.\n", 342 exch_to_ascii(EXCH_EXCH_TYPE), exch_to_ascii(exch_state.opcode)); 343 return -1; 344 } 345 if (exch_state.command_or_type != type) { 346 WHO_ARE_WE(); 347 fprintf(stderr, "Expected type 0x%x, got type 0x%x.\n", 348 type, exch_state.command_or_type); 349 return -1; 350 } 351 if (exch_state.length != netleng) { 352 fprintf(stderr, "Type 0x%x - expected length %d, received length %d.\n", 353 type, length, ntohs(exch_state.length)); 354 return -1; 355 } 356 while (length) { 357 if ((i = read(sock, location, length)) < 0) { 358 WHO_ARE_WE(); 359 perror("read"); 360 return -1; 361 } 362 length -= i; 363 location += i; 364 } 365 return 0; 366 } 367 368 int 369 api_exch_flush() 370 { 371 return outflush(); 372 } 373 374 int 375 api_exch_init(sock_number, ourname) 376 int sock_number; 377 char *ourname; 378 { 379 sock = sock_number; 380 strcpy(whoarewe, ourname); /* For error messages */ 381 382 my_sequence = your_sequence = 0; 383 384 conversation = CONTENTION; /* We don't know which direction */ 385 386 IBUFRESET(); 387 OBUFRESET(); 388 389 return 0; 390 } 391