PageRenderTime 76ms CodeModel.GetById 32ms app.highlight 38ms RepoModel.GetById 2ms app.codeStats 0ms

/sources/utils/network/socket.cpp

https://bitbucket.org/dbacchet/dbsdev
C++ | 690 lines | 508 code | 134 blank | 48 comment | 98 complexity | 107e68f1fdb87b69501579e744074f70 MD5 | raw file
  1
  2#include "basic_types.h"
  3
  4#if OS==OS_WINDOWS
  5
  6    #define WINVER       0x0501     // set windows xp as minimum required system
  7    #define _WIN32_WINNT 0x0501     // set windows xp as minimum required system
  8
  9    #include <winsock2.h>
 10    #include <ws2tcpip.h>
 11
 12    #define snprintf _snprintf
 13
 14#else
 15
 16    #include <arpa/inet.h>
 17    #include <sys/fcntl.h>
 18    #include <sys/types.h>
 19    #include <sys/socket.h>
 20    #include <sys/unistd.h>
 21    #include <sys/time.h>
 22    #include <netdb.h>
 23    #include <sys/ioctl.h>
 24    #include <errno.h>
 25
 26#endif
 27
 28
 29#include <cstdio>
 30
 31#include "socket.h"
 32
 33
 34// local functions
 35namespace
 36{
 37
 38#if OS==OS_WINDOWS
 39
 40	static void close(int socketHandler) 
 41    {
 42		closesocket(socketHandler);
 43	}
 44
 45
 46    // function provided here for compatibility with WindowsXP. On Vista and above this function is already present
 47	static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
 48	{
 49		if (af == AF_INET)
 50		{
 51			struct sockaddr_in in;
 52			memset(&in, 0, sizeof(in));
 53			in.sin_family = AF_INET;
 54			memcpy(&in.sin_addr, src, sizeof(struct in_addr));
 55			getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST);
 56			return dst;
 57		}
 58		else if (af == AF_INET6)
 59		{
 60			struct sockaddr_in6 in;
 61			memset(&in, 0, sizeof(in));
 62			in.sin6_family = AF_INET6;
 63			memcpy(&in.sin6_addr, src, sizeof(struct in_addr6));
 64			getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST);
 65			return dst;
 66		}
 67		return NULL;
 68	}
 69
 70#endif
 71
 72
 73    static uint16_t get_in_port(struct sockaddr* sa) 
 74    {
 75        if (sa->sa_family == AF_INET)
 76            return ntohs(((struct sockaddr_in*)sa)->sin_port);
 77        return ntohs(((struct sockaddr_in6*)sa)->sin6_port);
 78    }
 79
 80
 81    static uint16_t get_local_port(int socketHandler) 
 82    {
 83        struct sockaddr_storage sin;
 84
 85        #if OS==OS_WINDOWS
 86            int size;
 87        #else
 88            socklen_t size;
 89        #endif
 90
 91        size = sizeof(sin);
 92        if(getsockname(socketHandler, (struct sockaddr*)&sin, &size) == 0)
 93            return get_in_port((struct sockaddr*)&sin);
 94        else
 95            return 0;
 96    }
 97
 98
 99    static bool check_read_error(void) 
100    {
101        #if OS==OS_WINDOWS
102            if(WSAGetLastError() != WSAEWOULDBLOCK)
103                return true;
104        #else
105            if(errno != EAGAIN && errno != EWOULDBLOCK)
106                return true;
107        #endif
108        return false;
109    }
110
111
112    static void *get_in_addr(struct sockaddr *sa)
113    {
114        if (sa->sa_family == AF_INET)
115            return &(((struct sockaddr_in*)sa)->sin_addr);
116        return &(((struct sockaddr_in6*)sa)->sin6_addr);
117    }
118
119
120    static int get_local_address(int socketHandler, char address[network::ADDR_STRLEN]) 
121    {
122        struct sockaddr_storage sin;
123
124        #if OS==OS_WINDOWS
125            int size;
126        #else
127            socklen_t size;
128        #endif
129
130        size = sizeof(sin);
131        if(getsockname(socketHandler, (struct sockaddr*)&sin, &size) != 0)
132            return -1;
133
134        inet_ntop(sin.ss_family, get_in_addr((struct sockaddr *)&sin), address, network::ADDR_STRLEN);
135        
136        return 0;
137    }
138
139} // empty namespace
140
141
142
143
144namespace network
145{
146
147    /// Receives data and stores it in buffer until buffer_size reached.
148    /// @param buffer A pointer to a buffer where received data will be stored
149    /// @param buffer_size Size of the buffer (in bytes)
150    /// @return Size of received data or (-1) if Socket is non-blocking and there's no data received.
151    int tcp_read(Socket *sock, void* buffer, size_t buffer_size) 
152    {
153        return recv(sock->_socket_handler, (char*)buffer, buffer_size, 0);
154    }
155
156
157    /// Sends the data contained in buffer. Requires the Socket to be a CLIENT socket.
158    /// @pre Socket must be CLIENT
159    /// @param buffer A pointer to the data we want to send
160    /// @param size Length of the data to be sent (bytes)
161    bool tcp_send(Socket *sock, const void* buffer, size_t size) 
162    {
163
164        if(sock->_type != CLIENT)
165        {
166            printf("ERROR: tcp_send: Expected client socket (socket with host and port target)\n");
167            return false;
168        }
169
170        if(sock->_protocol != TCP)
171        {
172            printf("ERROR: tcp_send: Expected TCP protocol\n");
173            return false;
174        }
175
176        size_t sentData = 0;
177        while (sentData < size) 
178        {
179            int status = ::send(sock->_socket_handler, (const char*)buffer + sentData, size - sentData, 0);
180            if(status==-1 )
181            {
182                printf("ERROR: Error sending data\n");
183                return false;
184            }
185            sentData += status;
186        }
187        return true;
188    }
189
190
191    // same as tcp read: duplicated for naming consistency
192    int udp_read(Socket *sock, void* buffer, size_t buffer_size) 
193    {
194        return recv(sock->_socket_handler, (char*)buffer, buffer_size, 0);
195    }
196
197    /// Requires the socket to be UDP. Source host address and port are returned in hostFrom and
198    /// portFrom parameters. Data recieved is written in buffer address up to buffer_size.
199    /// @param buffer Pointer to a buffer where received data will be stored
200    /// @param buffer_size Size of the buffer
201    /// @param[out] hostFrom Here the function will store the address of the remote host
202    /// @param[out] portFrom Here the function will store the remote port
203    /// @return the length of the data recieved
204    int udp_read_from(Socket *sock, void *buffer, size_t buffer_size, char remote_host[ADDR_STRLEN], uint16_t *remote_port) 
205    {
206        if(sock->_protocol!=UDP)
207        {
208            printf("ERROR: udp_read_from: UDP socket required\n");
209            return -1;
210        }
211
212        struct sockaddr_storage addr;
213        socklen_t addrSize = sizeof(addr);
214        int status = recvfrom(sock->_socket_handler, (char*)buffer, buffer_size, 0, (struct sockaddr *)&addr, &addrSize);
215
216        if(status == -1) 
217        {
218            if(remote_host)
219                strcpy(remote_host,"");
220            if(remote_port)
221                *remote_port = 0;
222            if (check_read_error())
223            {
224                printf("ERROR: udp_read_from\n");
225                return -1;
226            }
227        }
228        else 
229        {
230            if(remote_port)
231                *remote_port = get_in_port((struct sockaddr*)&addr);
232            if(remote_host)
233                inet_ntop(addr.ss_family, get_in_addr((struct sockaddr *)&addr), remote_host, ADDR_STRLEN);
234        }
235
236        return status;
237    }
238
239
240    /// Sends the data contained in buffer to a given host:port. Requires the socket to be an UDP socket]
241    ///
242    /// @param buffer A pointer to the data we want to send
243    /// @param size Size of the data to send (bytes)
244    /// @param hostTo Target/remote host
245    /// @param portTo Target/remote port
246    bool udp_send_to(Socket *sock, const void *buffer, size_t size, const char *remote_host, uint16_t remote_port)
247    {
248
249        if(sock->_protocol != UDP)
250        {
251            printf("ERROR: udp_send_to: UDP socket required\n");
252            return false;
253        }
254
255        struct addrinfo conf, *res;
256        memset(&conf, 0, sizeof(conf));
257
258        conf.ai_socktype = SOCK_DGRAM;
259        conf.ai_family = AF_INET;
260        if (sock->_ip_ver==IP6)
261            conf.ai_family = AF_INET6;
262
263        char portStr[10];
264        snprintf(portStr, 10, "%u", remote_port);
265        
266        int status = getaddrinfo(remote_host, portStr, &conf, &res);
267        if(status != 0) 
268        {
269            printf("ERROR: udp_send_to: error setting addrInfo\n");
270            return false;
271        }
272
273        size_t sentBytes = 0;
274        while(sentBytes < size) 
275        {
276            int status = ::sendto(sock->_socket_handler, (const char*)buffer + sentBytes, size - sentBytes, 0, res->ai_addr, res->ai_addrlen);
277            if(status == -1)
278            {
279                printf("ERROR: udp_send_to: could not send the data\n");
280                return false;
281            }
282
283            sentBytes += status;
284        }
285
286        return true;
287    }
288
289
290    bool init_tcp_client_socket(Socket *sock) 
291    {
292
293        struct addrinfo conf, *res = NULL;
294        memset(&conf, 0, sizeof(conf));
295
296        conf.ai_socktype = SOCK_STREAM;
297        conf.ai_family = AF_UNSPEC;
298        if (sock->_ip_ver==IP4)
299            conf.ai_family = AF_INET;
300        else if (sock->_ip_ver==IP6)
301            conf.ai_family = AF_INET6;
302
303        char portStr[10];
304        snprintf(portStr, 10, "%u", sock->_remote_port);
305
306        int status = getaddrinfo(sock->_remote_host, portStr, &conf, &res);
307        if(status != 0) 
308        {
309            printf("ERROR: init_tcp_client_socket: Error setting addrInfo\n");
310            return false;
311        }
312
313        bool connected = false;
314        while(!connected && res) 
315        {
316            sock->_socket_handler = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
317
318            if(sock->_socket_handler != -1)
319            {
320                status = connect(sock->_socket_handler, res->ai_addr, res->ai_addrlen);
321                if(status != -1)
322                    connected = true;
323                else
324                    close(sock->_socket_handler);            
325            }
326            if(connected && sock->_ip_ver == ANY)
327                switch(res->ai_family) 
328                {
329                case AF_INET:
330                    sock->_ip_ver = IP4;
331                    break;
332                case AF_INET6:
333                    sock->_ip_ver = IP6;
334                    break;
335                }
336
337            res = res->ai_next;
338        }
339        
340        if(!connected)
341        {
342            printf("ERROR: init_tcp_client_socket: error in socket connection/bind\n");
343            return false;
344        }
345
346        if(!sock->_local_port)
347            sock->_local_port = get_local_port(sock->_socket_handler);
348
349        get_local_address(sock->_socket_handler, sock->_local_host);
350        return true;
351    }
352
353
354    bool init_tcp_server_socket(Socket *sock) 
355    {
356
357        struct addrinfo conf, *res = NULL;
358        memset(&conf, 0, sizeof(conf));
359
360        conf.ai_flags = AI_PASSIVE;
361        conf.ai_socktype = SOCK_STREAM;
362        conf.ai_family = AF_INET;
363        if (sock->_ip_ver==IP6)
364            conf.ai_family = AF_INET6;
365
366        char portStr[10];
367        snprintf(portStr, 10, "%u", sock->_local_port);
368        const char* host = NULL;
369        if (0!=strcmp(sock->_local_host,""))
370            host = sock->_local_host;
371
372        int status = getaddrinfo(host, portStr, &conf, &res);
373        if(status != 0) 
374        {
375            printf("ERROR: init_tcp_server_socket() unable to set addrinfo\n");
376            return false;
377        }
378
379        bool connected = false;
380
381        while(!connected && res) 
382        {
383            sock->_socket_handler = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
384            if(sock->_socket_handler != -1)
385            {
386                int yes = 1;
387                #if OS==OS_WINDOWS
388                    char *optval = (char*)&yes;
389                #else
390                    void *optval = (void*)&yes;
391                #endif
392                if (setsockopt(sock->_socket_handler, SOL_SOCKET, SO_REUSEADDR, optval, sizeof(int)) == -1)
393                {
394                    printf("ERROR: init_tcp_server_socket: Error establishing socket options\n");
395                    return false;
396                }
397
398                if (bind(sock->_socket_handler, res->ai_addr, res->ai_addrlen) == -1)
399                    close(sock->_socket_handler);
400                else
401                    connected = true;
402
403                if (listen(sock->_socket_handler, sock->_listen_queue_size) == -1)
404                {
405                    printf("ERROR: init_tcp_server_socket: could not start listening\n");
406                    return false;
407                }
408            }
409            res = res->ai_next;
410
411        }
412
413        if(!connected)
414        {
415            printf("ERROR: init_tcp_server_socket: error in socket connection/bind\n");
416            return false;
417        }
418
419        return true;
420    }
421
422
423    bool init_udp_socket(Socket *sock) 
424    {
425
426        struct addrinfo conf, *res = NULL;
427        memset(&conf, 0, sizeof(conf));
428
429        conf.ai_flags = AI_PASSIVE;
430        conf.ai_socktype = SOCK_DGRAM;
431        conf.ai_family = AF_UNSPEC;
432        if (sock->_ip_ver==IP4) conf.ai_family = AF_INET;
433        if (sock->_ip_ver==IP6) conf.ai_family = AF_INET6;
434
435        char portStr[10];
436        snprintf(portStr, 10, "%u", sock->_local_port);
437        const char* host = NULL;
438        if (0!=strcmp(sock->_local_host,""))
439            host = sock->_local_host;
440
441        int status = getaddrinfo(host, portStr, &conf, &res);
442        if(status != 0) 
443        {
444            printf("ERROR: init_udp_socket: Error setting addrInfo\n");
445            return false;
446        }
447
448        bool connected = false;
449        while(!connected && res) 
450        {
451            sock->_socket_handler = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
452
453            if(sock->_socket_handler != -1)
454            {
455                if (bind(sock->_socket_handler, res->ai_addr, res->ai_addrlen) == -1)
456                    close(sock->_socket_handler);
457                else
458                    connected = true;
459            }
460
461            if(connected && sock->_ip_ver == ANY)
462                switch(res->ai_family) 
463                {
464                    case AF_INET:
465                        sock->_ip_ver = IP4;
466                        break;
467
468                    case AF_INET6:
469                        sock->_ip_ver = IP6;
470                        break;
471                }
472
473            res = res->ai_next;
474        }
475
476        if(!connected)
477        {
478            printf("ERROR: init_udp_socket: error in socket connection/bind\n");
479            return false;
480        }
481
482        if(!sock->_local_port)
483            sock->_local_port = get_local_port(sock->_socket_handler);
484        get_local_address(sock->_socket_handler, sock->_local_host);
485
486        return true;
487    }
488
489
490    /// Sets the Socket as blocking (if blocking is true) or as non-blocking (otherwise)
491    /// @param blocking true to set the Socket as blocking; false to set the Socket as non-blocking
492    void socket_set_blocking(Socket *sock, bool blocking) 
493    {
494        sock->_blocking = blocking;
495
496        int result = -1;
497
498        #if OS==OS_WINDOWS
499
500            u_long non_blocking = !blocking;
501            result = ioctlsocket(sock->_socket_handler, FIONBIO, &non_blocking);
502            if(result!=0)
503                result = -1;
504        #else
505
506            int flags = fcntl(sock->_socket_handler, F_GETFL);
507
508            if(blocking)
509                result = fcntl(sock->_socket_handler, F_SETFL, flags & ~O_NONBLOCK);
510            else
511                result = fcntl(sock->_socket_handler, F_SETFL, flags | O_NONBLOCK);
512        #endif
513    }
514
515
516    /// Creates a new CLIENT socket to handle the communication (send/recieve data) of
517    /// this accepted connection. Requires the socket to be a SERVER TCP socket.
518    /// @return A CLIENT socket that handles the new connection
519    Socket* tcp_server_accept(Socket *server) 
520    {
521        if(server->_protocol!=TCP || server->_type!=SERVER)
522        {
523            printf("ERROR: tcp_server_accept: required a TCP server socket\n");
524            return NULL;
525        }
526
527        struct sockaddr_storage incoming_addr;
528
529        #if OS==OS_WINDOWS
530            int addrSize = sizeof(incoming_addr);
531        #else
532            socklen_t addrSize = sizeof(incoming_addr);
533        #endif
534
535        int new_handler = ::accept(server->_socket_handler, (struct sockaddr *)&incoming_addr, &addrSize);
536
537        if(new_handler == -1)
538            return NULL;
539
540        Socket* acceptSocket = new Socket();
541        acceptSocket->_socket_handler = new_handler;
542        inet_ntop(incoming_addr.ss_family, get_in_addr((struct sockaddr *)&incoming_addr), acceptSocket->_remote_host, ADDR_STRLEN);
543        acceptSocket->_remote_port = get_in_port((struct sockaddr *)&incoming_addr);
544        acceptSocket->_local_port = get_local_port(acceptSocket->_socket_handler);
545        get_local_address(acceptSocket->_socket_handler, acceptSocket->_local_host);
546        acceptSocket->_protocol = server->_protocol;
547        acceptSocket->_ip_ver = server->_ip_ver;
548        acceptSocket->_type = CLIENT;
549        acceptSocket->_listen_queue_size = 0;
550        socket_set_blocking(acceptSocket,server->_blocking);
551
552        return acceptSocket;
553    }
554
555
556    /// disconnect (close) the socket
557    void socket_disconnect(Socket *sock) 
558    {
559        if (sock && sock->_socket_handler!=-1)
560        {
561            close(sock->_socket_handler);
562            sock->_socket_handler = -1;            
563        }
564    }
565
566
567    /// Creates a socket, connect it (if TCP) and sets it ready to send to hostTo:portTo.
568    /// The local port of the socket is choosen by OS.
569    /// @param hostTo the target/remote host
570    /// @param portTo the target/remote port
571    /// @param protocol the protocol to be used (TCP or UDP). TCP by default.
572    /// @param ipVer the IP version to be used (IP4, IP6 or ANY). ANY by default.
573    Socket* create_tcp_client(const char *remote_host, uint16_t remote_port, IPVer ipVer)
574    {
575        Socket *socket    = new Socket();
576        strcpy(socket->_remote_host,remote_host);
577        socket->_remote_port   = remote_port;
578        socket->_local_port = 0;
579        socket->_protocol = TCP;
580        socket->_ip_ver    = ipVer;
581        socket->_type     = CLIENT;
582        socket->_blocking = true; 
583        socket->_listen_queue_size = 0;
584        
585        if (init_tcp_client_socket(socket))
586            return socket;
587        else
588        {
589            delete socket;
590            return NULL;
591        }
592    }
593
594
595    ///Creates a socket, binds it to portFrom port and listens for connections (if TCP).
596    ///@param portFrom the local port the socket will be bound to
597    ///@param protocol the protocol to be used (TCP or UDP). TCP by default.
598    ///@param ipVer the IP version to be used (IP4, IP6 or ANY). IP4 by default.
599    ///@param hostFrom the local address to be binded to (example: "localhost" or "127.0.0.1"). Empty (by default) means all available addresses.
600    ///@param listen_queue_size the size of the internal buffer of the SERVER TCP socket where the connection requests are stored until accepted
601    Socket* create_tcp_server(uint16_t local_port, const char *local_host, IPVer ipVer, uint16_t listen_queue_size)
602    {
603        Socket *socket = new Socket();    
604        strcpy(socket->_local_host,local_host);
605        socket->_remote_port   = 0;
606        socket->_local_port = local_port;
607        socket->_protocol = TCP;
608        socket->_ip_ver    = ipVer;
609        socket->_type     = SERVER;
610        socket->_blocking = true;
611        socket->_listen_queue_size = listen_queue_size;
612
613        if (init_tcp_server_socket(socket))
614            return socket;
615        else
616        {
617            delete socket;
618            return NULL;
619        }
620    }
621
622    /// This client constructor for UDP Sockets allows to expecify the local port the socket
623    /// will be bound to. It sets the socket ready to send data to hostTo:portTo.
624    /// @param hostTo the target/remote host
625    /// @param portTo the target/remote port
626    /// @param portFrom the local port the socket will be bound to
627    /// @param ipVer the IP version to be used (IP4, IP6 or ANY). ANY by default.
628    Socket* create_udp_client(const char *remote_host, uint16_t remote_port, uint16_t local_port, IPVer ipVer)
629    {
630        Socket *socket    = new Socket();    
631        strcpy(socket->_remote_host,remote_host);
632        socket->_remote_port   = remote_port;
633        socket->_local_port = local_port;
634        socket->_protocol = UDP;
635        socket->_ip_ver    = ipVer;
636        socket->_type     = CLIENT;
637        socket->_blocking = true;
638        socket->_listen_queue_size = 0;
639
640        if (init_udp_socket(socket))
641            return socket;
642        else
643        {
644            delete socket;
645            return NULL;
646        }
647    }
648
649    void print_socket_data(Socket *sock)
650    {
651        printf("Socket data: \n"
652               "   _local_host = %s\n"
653               "   _local_port = %u\n"
654               "   _remote_host = %s\n"
655               "   _remote_port = %u\n"
656               "   _protocol = %d\n"
657               "   _ip_ver = %d\n"
658               "   _type = %d\n"
659               "   _blocking = %s\n"
660               "   _listen_queue_size = %u\n", sock->_local_host,
661                                               sock->_local_port,
662                                               sock->_remote_host,
663                                               sock->_remote_port,
664                                               sock->_protocol,
665                                               sock->_ip_ver,
666                                               sock->_type,
667                                               sock->_blocking ? "true" : "false",
668                                               sock->_listen_queue_size);
669                                             
670    }
671
672}
673
674
675namespace network_globals
676{
677    bool init() 
678    {
679        #if OS==OS_WINDOWS
680            WSADATA wsaData;
681            if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0)
682                return false;
683        #endif
684        return true;
685    }
686    
687}
688
689
690