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