1 /*-----------------------------------------------------------------------
2
3 File : cio_network.c
4
5 Author: Stephan Schulz (schulz@eprover.org)
6
7 Contents
8
9 Helper code for TCP connections and "message" based communication
10 over TCP (each message corresponds to a transaction request and is
11 packaged as a message to allow parsing in whole).
12
13
14 Copyright 2011 by the author.
15 This code is released under the GNU General Public Licence.
16 See the file COPYING in the main CLIB directory for details.
17 Run "eprover -h" for contact information.
18
19 Changes
20
21 <1> New
22
23 -----------------------------------------------------------------------*/
24
25 #include <netinet/in.h>
26 #include <sys/socket.h>
27 #include <netdb.h>
28
29 #include "cio_network.h"
30
31
32
33 /*---------------------------------------------------------------------*/
34 /* Global Variables */
35 /*---------------------------------------------------------------------*/
36
37 #define TCP_BACKLOG 10
38 #define TCP_BUF_SIZE 1025
39
40 /*---------------------------------------------------------------------*/
41 /* Forward Declarations */
42 /*---------------------------------------------------------------------*/
43
44
45 /*---------------------------------------------------------------------*/
46 /* Internal Functions */
47 /*---------------------------------------------------------------------*/
48
49
50
51 /*-----------------------------------------------------------------------
52 //
53 // Function: create_server_sock_nofail()
54 //
55 // Try to create a bound server socket. Return -1 on failure, the
56 // socket identifier on success.
57 //
58 // Global Variables: -
59 //
60 // Side Effects : Creates and binds the socket.
61 //
62 /----------------------------------------------------------------------*/
63
create_server_sock_nofail(int port)64 int create_server_sock_nofail(int port)
65 {
66 int sock = socket(PF_INET,SOCK_STREAM, IPPROTO_TCP);
67 int res;
68 struct sockaddr_in addr;
69
70 if(sock == -1)
71 {
72 return -1;
73 }
74
75 addr.sin_family = AF_INET;
76 addr.sin_port = htons(port);
77 addr.sin_addr.s_addr = INADDR_ANY;
78 memset(&addr.sin_zero, 0, sizeof(addr.sin_zero));
79
80 int yes = 1;
81 if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
82 {
83 return -1;
84 }
85
86 res = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
87
88 if(res == -1)
89 {
90 return -1;
91 }
92 return sock;
93 }
94
95
96
97 /*-----------------------------------------------------------------------
98 //
99 // Function: create_client_sock_nofail()
100 //
101 // Try to create a client socket connected to the provided
102 // host. Return negative value on failure, the socket identifier on
103 // success. The error return is -1 for errno-errors, -2-error for
104 // gai_error-errors.
105 //
106 // Global Variables: -
107 //
108 // Side Effects : Creates and connects the socket.
109 //
110 /----------------------------------------------------------------------*/
111
create_client_sock_nofail(char * host,int port)112 int create_client_sock_nofail(char* host, int port)
113 {
114 int sock = -1;
115 int res;
116 struct addrinfo hints, *addr, *iter;
117 char portstr[16];
118
119 sprintf(portstr, "%d", port);
120 memset(&hints, 0, sizeof(hints));
121 hints.ai_family = PF_UNSPEC;
122 hints.ai_socktype = SOCK_STREAM;
123 res = getaddrinfo(host, portstr, &hints, &addr);
124 if(res)
125 {
126 return -2-res;
127 }
128 for(iter=addr; iter; iter = iter->ai_next)
129 {
130 sock = socket(iter->ai_family, iter->ai_socktype,
131 iter->ai_protocol);
132 if (sock < 0)
133 {
134 continue;
135 }
136 if (connect(sock, iter->ai_addr, iter->ai_addrlen) < 0)
137 {
138 close(sock);
139 sock = -1;
140 continue;
141 }
142 }
143 return sock;
144 }
145
146
147
148
149 /*---------------------------------------------------------------------*/
150 /* Exported Functions */
151 /*---------------------------------------------------------------------*/
152
153
154
155 /*-----------------------------------------------------------------------
156 //
157 // Function: TCPMsgAlloc()
158 //
159 // Allocate an initialized TCP message cell.
160 //
161 // Global Variables: -
162 //
163 // Side Effects : Memory management
164 //
165 /----------------------------------------------------------------------*/
166
TCPMsgAlloc(void)167 TCPMsg_p TCPMsgAlloc(void)
168 {
169 TCPMsg_p res = TCPMsgCellAlloc();
170
171 res->content = DStrAlloc();
172 res->len = -1;
173 res->transmission_count = 0;
174
175 return res;
176 }
177
178 /*-----------------------------------------------------------------------
179 //
180 // Function: TCPMsgFree()
181 //
182 // Free a TCP message cell.
183 //
184 // Global Variables: -
185 //
186 // Side Effects : Memory management.
187 //
188 /----------------------------------------------------------------------*/
189
TCPMsgFree(TCPMsg_p junk)190 void TCPMsgFree(TCPMsg_p junk)
191 {
192 DStrFree(junk->content);
193 TCPMsgCellFree(junk);
194 }
195
196
197 /*-----------------------------------------------------------------------
198 //
199 // Function: TCPMsgPack()
200 //
201 // Take a string and convert it into a newly allocated TCP Msg.
202 //
203 // Global Variables: -
204 //
205 // Side Effects : Memory allocation
206 //
207 /----------------------------------------------------------------------*/
208
TCPMsgPack(char * str)209 TCPMsg_p TCPMsgPack(char* str)
210 {
211 uint32_t len;
212 TCPMsg_p handle = TCPMsgAlloc();
213
214 len = strlen(str)+sizeof(len);
215 DStrAppendStr(handle->content, "0000");
216 *((uint32_t*)DStrAddress(handle->content, 0)) = htonl(len);
217 DStrAppendStr(handle->content, str);
218 handle->len = len;
219
220 return handle;
221 }
222
223
224 /*-----------------------------------------------------------------------
225 //
226 // Function: TCPMsgUnpack()
227 //
228 // Given a TCP message, return the string and destroy the
229 // container. If this ever becomes measurable, we can make this
230 // faster by avoiding the copy...
231 //
232 // Global Variables: -
233 //
234 // Side Effects : Memory operations
235 //
236 /----------------------------------------------------------------------*/
237
TCPMsgUnpack(TCPMsg_p msg)238 char* TCPMsgUnpack(TCPMsg_p msg)
239 {
240 char *res = SecureStrdup(DStrAddress(msg->content, sizeof(uint32_t)));
241 TCPMsgFree(msg);
242
243 return res;
244 }
245
246
247 /*-----------------------------------------------------------------------
248 //
249 // Function: TCPMsgWrite()
250 //
251 // Send the message over the socket. Return NWError, NWIncomplete,
252 // or NWSuccess depending on wether the transmission was partial,
253 // complete or a failure.
254 //
255 // Global Variables: -
256 //
257 // Side Effects : IO, memory.
258 //
259 /----------------------------------------------------------------------*/
260
TCPMsgWrite(int sock,TCPMsg_p msg)261 MsgStatus TCPMsgWrite(int sock, TCPMsg_p msg)
262 {
263 int remaining, res;
264
265 remaining = msg->len-msg->transmission_count;
266
267 res = write(sock,
268 DStrAddress(msg->content, msg->transmission_count),
269 remaining);
270 if(res < 0 )
271 {
272 return NWError;
273 }
274 msg->transmission_count+=res;
275 if(!TCP_MSG_COMPLETE(msg))
276 {
277 return NWIncomplete;
278 }
279 return NWSuccess;
280 }
281
282
283 /*-----------------------------------------------------------------------
284 //
285 // Function: TCPMsgRead()
286 //
287 // Receive a (partial) TCP message. Return NWError, NWIncomplete,
288 // or NWSuccess depending on wether the transmission was partial,
289 // complete or a failure. Return NWConnClosed if the connection was
290 // closed. This assumes that the message
291 // itself is plain ASCII string (i.e. no '\0' in the message),
292 // although it probably works otherwise.
293 //
294 // Global Variables: -
295 //
296 // Side Effects : IO, memory
297 //
298 /----------------------------------------------------------------------*/
299
TCPMsgRead(int sock,TCPMsg_p msg)300 MsgStatus TCPMsgRead(int sock, TCPMsg_p msg)
301 {
302 char buffer[TCP_BUF_SIZE];
303 uint32_t len;
304 int res;
305
306 /* Handle header */
307 if(msg->transmission_count < (int)sizeof(uint32_t))
308 {
309 res = read(sock,
310 msg->len_buf+msg->transmission_count,
311 sizeof(uint32_t)-msg->transmission_count);
312 printf("read(Size)=%d\n", res);
313 if(res < 0)
314 {
315 return NWError;
316 }
317 if(res ==0)
318 {
319 return NWConnClosed;
320 }
321 msg->transmission_count += res;
322 if(msg->transmission_count < (int)sizeof(uint32_t))
323 {
324 return 0;
325 }
326 memcpy(&len, msg->len_buf, sizeof(uint32_t));
327 len = ntohl(len);
328 printf("Message expected with %d bytes\n", len);
329 msg->len = len;
330 DStrAppendBuffer(msg->content, msg->len_buf, sizeof(uint32_t));
331 }
332 /* Rest */
333 len = MIN(TCP_BUF_SIZE-1, msg->len - msg->transmission_count);
334 res = read(sock, buffer, len);
335 printf("read(msg)=%d\n", res);
336 if(res < 0)
337 {
338 return NWError;
339 }
340 if(res ==0)
341 {
342 return NWConnClosed;
343 }
344 buffer[len] = '\0';
345 DStrAppendStr(msg->content, buffer);
346 msg->transmission_count += res;
347
348 if(!TCP_MSG_COMPLETE(msg))
349 {
350 return NWIncomplete;
351 }
352 return NWSuccess;
353 }
354
355
356
357 /*-----------------------------------------------------------------------
358 //
359 // Function: TCPMsgSend()
360 //
361 // Send the message over the connection represented
362 // by socket. This will block until transmission is complete. Return
363 // status.
364 //
365 // Global Variables: -
366 //
367 // Side Effects : Network traffic
368 //
369 /----------------------------------------------------------------------*/
370
TCPMsgSend(int sock,TCPMsg_p msg)371 MsgStatus TCPMsgSend(int sock, TCPMsg_p msg)
372 {
373 int res = 0;
374
375 while(res != NWSuccess)
376 {
377 res = TCPMsgWrite(sock, msg);
378 if(res==NWError)
379 {
380 break;
381 }
382 }
383 return res;
384 }
385
386
387
388
389 /*-----------------------------------------------------------------------
390 //
391 // Function: TCPMsgRecv()
392 //
393 // Receive and return a message and return it. This will block until
394 // transmission terminates.
395 //
396 // Global Variables: -
397 //
398 // Side Effects : Memory
399 //
400 /----------------------------------------------------------------------*/
401
TCPMsgRecv(int sock,MsgStatus * res)402 TCPMsg_p TCPMsgRecv(int sock, MsgStatus *res)
403 {
404 TCPMsg_p msg = TCPMsgAlloc();
405
406 *res = 0;
407
408 while(*res != NWSuccess)
409 {
410 *res = TCPMsgRead(sock, msg);
411 if(*res == NWError || *res == NWConnClosed)
412 {
413 break;
414 }
415 }
416 return msg;
417 }
418
419
420
421
422 /*-----------------------------------------------------------------------
423 //
424 // Function: TCPStringSend()
425 //
426 // Send the string as a TCP message over the connection represented
427 // by socket. This will block until transmission is complete. Return
428 // status.
429 //
430 // Global Variables: -
431 //
432 // Side Effects : Network traffic
433 //
434 /----------------------------------------------------------------------*/
435
TCPStringSend(int sock,char * str,bool err)436 MsgStatus TCPStringSend(int sock, char* str, bool err)
437 {
438 TCPMsg_p msg = TCPMsgPack(str);
439 MsgStatus res = TCPMsgSend(sock, msg);
440
441 if(err && res!=NWSuccess)
442 {
443 SysError("Could not send string message", SYS_ERROR);
444 }
445 TCPMsgFree(msg);
446
447 return res;
448 }
449
450
451 /*-----------------------------------------------------------------------
452 //
453 // Function: TCPStringRecv()
454 //
455 // Receive a message string, unpack it, and return the message
456 // content (or NULL on failure).
457 //
458 // Global Variables: -
459 //
460 // Side Effects : Memory
461 //
462 /----------------------------------------------------------------------*/
463
TCPStringRecv(int sock,MsgStatus * res,bool err)464 char* TCPStringRecv(int sock, MsgStatus* res, bool err)
465 {
466 TCPMsg_p msg = TCPMsgRecv(sock, res);
467
468 if(*res != NWSuccess)
469 {
470 if(err)
471 {
472 SysError("Could not receive string message", SYS_ERROR);
473 }
474 TCPMsgFree(msg);
475 return NULL;
476 }
477 return TCPMsgUnpack(msg);
478 }
479
480
481 /*-----------------------------------------------------------------------
482 //
483 // Function: TCPStringSendX()
484 //
485 // Send a string, fail on error.
486 //
487 // Global Variables: -
488 //
489 // Side Effects : Indirect
490 //
491 /----------------------------------------------------------------------*/
492
TCPStringSendX(int sock,char * str)493 void TCPStringSendX(int sock, char* str)
494 {
495 TCPStringSend(sock, str, true);
496 }
497
498
499 /*-----------------------------------------------------------------------
500 //
501 // Function: TCPStringRecvX()
502 //
503 // Read and return a string.
504 //
505 // Global Variables: -
506 //
507 // Side Effects : Indirect
508 //
509 /----------------------------------------------------------------------*/
510
TCPStringRecvX(int sock)511 char* TCPStringRecvX(int sock)
512 {
513 MsgStatus res;
514
515 return TCPStringRecv(sock, &res, true);
516 }
517
518
519 /*-----------------------------------------------------------------------
520 //
521 // Function: CreateServerSock()
522 //
523 // Create a server socket bound to the given port and return
524 // it. Fail with error message if the port cannot be creates.
525 //
526 // Global Variables: -
527 //
528 // Side Effects : Creates and binds socket
529 //
530 /----------------------------------------------------------------------*/
531
CreateServerSock(int port)532 int CreateServerSock(int port)
533 {
534 int sock = create_server_sock_nofail(port);
535
536 if(sock==-1)
537 {
538 TmpErrno = errno;
539 SysError("Cannot create socket for port %d", SYS_ERROR, port);
540 }
541 return sock;
542 }
543
544
545 /*-----------------------------------------------------------------------
546 //
547 // Function: Listen()
548 //
549 // Thin wrapper around listen() terminating with an error message if
550 // it fails.
551 //
552 // Global Variables: -
553 //
554 // Side Effects : Socket operation
555 //
556 /----------------------------------------------------------------------*/
557
Listen(int sock)558 void Listen(int sock)
559 {
560 int res = listen(sock, TCP_BACKLOG);
561
562 if(res == -1)
563 {
564 TmpErrno = errno;
565 SysError("Failed to switch socket %d to listening",
566 SYS_ERROR, socket);
567 }
568 }
569
570
571 /*-----------------------------------------------------------------------
572 //
573 // Function: CreateClientSock()
574 //
575 // Create a socket connected to the given host and port. Return sock
576 // or terminate with error on fail.
577 //
578 // Global Variables:
579 //
580 // Side Effects :
581 //
582 /----------------------------------------------------------------------*/
583
CreateClientSock(char * host,int port)584 int CreateClientSock(char* host, int port)
585 {
586 int sock = create_client_sock_nofail(host, port);
587
588 if(sock == -1)
589 {
590 TmpErrno = errno;
591 SysError("Could not create connected socket", SYS_ERROR);
592 }
593 if(sock <= -2)
594 {
595 Error("Could not resolve address (%s)", SYS_ERROR,
596 gai_strerror(-(sock+2)));
597 }
598 return sock;
599 }
600
601
602
603
604 /*---------------------------------------------------------------------*/
605 /* End of File */
606 /*---------------------------------------------------------------------*/
607
608
609