src/: +streamfer*
[nethome.git] / src / socket.C
1 #include "socket.h"
2 #include "stringf.h"
3 #include <netdb.h>
4 #include <netinet/ip.h>
5 #include <netinet/tcp.h>
6
7 int socket_bind(string host_port_str) {
8   const char *cs(strrchr(host_port_str.c_str(),':'));
9   const string node(!cs?"":host_port_str.substr(0,cs-host_port_str.c_str()));
10   const string service(!cs?host_port_str:cs+1);
11   struct addrinfo hints={}; // designated initializer: error: missing initializer for member ... [-Werror=missing-field-initializers]
12   hints.ai_family=AF_UNSPEC;    /* Allow IPv4 or IPv6 */
13   hints.ai_socktype=SOCK_STREAM;
14   hints.ai_flags=AI_PASSIVE|AI_ADDRCONFIG;    /* For wildcard IP address */
15   struct addrinfo *result;
16   int err(getaddrinfo(node.empty()?NULL:node.c_str(),service.c_str(),&hints,&result));
17   if (err)
18     fatal("<%s>:<%s>: %s",node.c_str(),service.c_str(),gai_strerror(err));
19   int fd=-1;
20   struct addrinfo *rp;
21   for (rp=result;rp;rp=rp->ai_next) {
22     fd=socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol);
23     if (fd==-1)
24       continue;
25     static const int int1(1);
26     err=setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&int1,sizeof(int1));
27     assert(!err);
28     if (bind(fd,rp->ai_addr,rp->ai_addrlen)==0)
29       break;
30     err=close(fd);
31     assert(!err);
32   }
33   if (rp==NULL)
34     fatal("Cannot bind(): <%s>:<%s>: %m",node.c_str(),service.c_str());
35   assert(fd!=-1);
36   freeaddrinfo(result);
37   err=listen(fd,SOMAXCONN);
38   assert(!err);
39   return fd;
40 }
41
42 string sockaddr_string(const struct sockaddr *sockaddrp,socklen_t socklen) {
43   char hostname[NI_MAXHOST];
44   char servname[NI_MAXSERV];
45   const int err=getnameinfo(sockaddrp,socklen,hostname,sizeof(hostname),servname,sizeof(servname),NI_NUMERICSERV/*flags*/);
46   assert(!err);
47   return stringf("%s:%s",hostname,servname);
48 }
49
50 string socket_name(int socket_fd) {
51   struct sockaddr sockaddr;
52   socklen_t socklen(sizeof(sockaddr));
53   const int err(getsockname(socket_fd,&sockaddr,&socklen));
54   assert(!err);
55   return sockaddr_string(&sockaddr,socklen);
56 }
57
58 static void socket_setopt(int fd) {
59   static const int int1(1);
60   int err;
61   err=setsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,&int1,sizeof(int1));
62   assert(!err);
63   static const uint8_t tos(IPTOS_LOWDELAY);
64   err=setsockopt(fd,IPPROTO_IP,IP_TOS,&tos,sizeof(tos));
65   assert(!err);
66   err=setsockopt(fd,IPPROTO_IP,TCP_NODELAY,&int1,sizeof(int1));
67   assert(!err);
68 }
69
70 int socket_accept(int listen_fd,function<void(int client_fd,string addr)> msgfunc) {
71   struct sockaddr sockaddr;
72   socklen_t socklen(sizeof(sockaddr));
73   const int client_fd(accept(listen_fd,&sockaddr,&socklen));
74   assert(client_fd>=0);
75   msgfunc(client_fd,sockaddr_string(&sockaddr,socklen));
76   socket_setopt(client_fd);
77   return client_fd;
78 }
79
80 int socket_connect(const string &host_port_str,unsigned retries) {
81   const char *cs(strrchr(host_port_str.c_str(),':'));
82   if (!cs)
83     fatal("Error parsing <host>:<port>: %s",host_port_str.c_str());
84   const string node(host_port_str.substr(0,cs-host_port_str.c_str()));
85   const string service(cs+1);
86   struct addrinfo hints={}; // designated initializer: error: missing initializer for member ... [-Werror=missing-field-initializers]
87   hints.ai_family=AF_UNSPEC;    /* Allow IPv4 or IPv6 */
88   hints.ai_socktype=SOCK_STREAM;
89   struct addrinfo *result;
90   int err=getaddrinfo(node.c_str(),service.c_str(),&hints,&result);
91   if (err)
92     fatal("Error parsing node+service: <%s>:<%s>: %s",node.c_str(),service.c_str(),gai_strerror(err));
93   int fd;
94   struct addrinfo *rp;
95   for (unsigned retryno=0;retryno<1+retries;++retryno) {
96     if (retryno) {
97       warning("Sleeping 1 second for connect retry #%u/%u",retryno,retries);
98       err=sleep(1);
99       assert(!err);
100     }
101     for (rp=result;rp;rp=rp->ai_next) {
102       fd=socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol);
103       if (fd==-1)
104         continue;
105       if (connect(fd,rp->ai_addr,rp->ai_addrlen)==0)
106         break;
107       err=close(fd);
108       assert(!err);
109     }
110     if (rp==NULL)
111       warning("Could not connect(): <%s>:<%s>: %m",node.c_str(),service.c_str());
112     else
113       break;
114   }
115   freeaddrinfo(result);
116   if (rp==NULL)
117     fatal("Could not connect(), giving up");
118   socket_setopt(fd);
119   return fd;
120 }