modified: makefile
[GalaxyCodeBases.git] / c_cpp / etc / mbuffer / network.c
blob5c995bb45d86e354726a4eacc3b6fc8acb001551
1 /*
2 * Copyright (C) 2000-2009, Thomas Maier-Komor
4 * This is the source code of mbuffer.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "config.h"
22 #ifdef HAVE_ALLOCA_H
23 #include <alloca.h>
24 #elif defined __GNUC__
25 #define alloca __builtin_alloca
26 #elif defined _AIX
27 #define alloca __alloca
28 #else
29 #include <stddef.h>
30 void *alloca(size_t);
31 #endif
34 #include <assert.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/un.h>
43 #include <unistd.h>
44 #include <netdb.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
48 #include "dest.h"
49 #include "network.h"
50 #include "log.h"
52 extern int In;
53 int32_t TCPBufSize = 1 << 20;
54 #if defined(PF_INET6) && defined(PF_UNSPEC)
55 int AddrFam = PF_UNSPEC;
56 #else
57 int AddrFam = PF_INET;
58 #endif
61 static void setTCPBufferSize(int sock, unsigned buffer)
63 int err;
64 int32_t osize, size;
65 socklen_t bsize = sizeof(osize);
67 assert(buffer == SO_RCVBUF || buffer == SO_SNDBUF);
68 err = getsockopt(sock,SOL_SOCKET,buffer,&osize,&bsize);
69 assert((err == 0) && (bsize == sizeof(osize)));
70 if (osize < TCPBufSize) {
71 size = TCPBufSize;
72 do {
73 err = setsockopt(sock,SOL_SOCKET,buffer,(void *)&size,sizeof(size));
74 size >>= 1;
75 } while ((-1 == err) && (errno == ENOMEM) && (size > osize));
76 if (err == -1) {
77 warningmsg("unable to set socket buffer size: %s\n",strerror(errno));
78 return;
81 bsize = sizeof(size);
82 err = getsockopt(sock,SOL_SOCKET,buffer,&size,&bsize);
83 assert(err != -1);
84 if (buffer == SO_RCVBUF)
85 infomsg("set TCP receive buffer size to %d\n",size);
86 else
87 infomsg("set TCP send buffer size to %d\n",size);
91 #ifdef HAVE_GETADDRINFO
93 void initNetworkInput(const char *addr)
95 char *host, *port;
96 struct addrinfo hint, *pinfo = 0, *x, *cinfo = 0;
97 int err, sock = -1, l;
99 debugmsg("initNetworkInput(\"%s\")\n",addr);
100 l = strlen(addr) + 1;
101 host = alloca(l);
102 memcpy(host,addr,l);
103 port = strrchr(host,':');
104 if (port == 0) {
105 port = host;
106 host = 0;
107 } else if (port == host) {
108 port = host + 1;
109 host = 0;
110 } else {
111 *port = 0;
112 ++port;
113 bzero(&hint,sizeof(hint));
114 hint.ai_family = AddrFam;
115 hint.ai_protocol = IPPROTO_TCP;
116 hint.ai_socktype = SOCK_STREAM;
117 #ifdef __FreeBSD__
118 hint.ai_flags = AI_ADDRCONFIG;
119 #else
120 hint.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED;
121 #endif
122 err = getaddrinfo(host,0,&hint,&cinfo);
123 if (err != 0)
124 fatal("unable to resolve address information for expected host '%s': %s\n",host,gai_strerror(err));
126 bzero(&hint,sizeof(hint));
127 hint.ai_family = AddrFam;
128 hint.ai_protocol = IPPROTO_TCP;
129 hint.ai_socktype = SOCK_STREAM;
130 hint.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
131 err = getaddrinfo(0,port,&hint,&pinfo);
132 if (err != 0)
133 fatal("unable to get address information for port/service '%s': %s\n",port,gai_strerror(err));
134 assert(pinfo);
135 for (x = pinfo; x; x = x->ai_next) {
136 int reuse_addr = 1;
137 debugmsg("creating socket for address familiy %d\n",x->ai_family);
138 sock = socket(x->ai_family, SOCK_STREAM, 0);
139 if (sock == -1) {
140 warningmsg("unable to create socket for input: %s\n",strerror(errno));
141 continue;
143 if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)))
144 warningmsg("cannot set socket to reuse address: %s\n",strerror(errno));
145 if (0 == bind(sock, x->ai_addr, x->ai_addrlen)) {
146 debugmsg("successfully bound socket - address length %d\n",x->ai_addrlen);
147 break;
149 warningmsg("could not bind to socket for network input: %s\n",strerror(errno));
150 (void) close(sock);
152 if (x == 0)
153 fatal("Unable to initialize network input.\n");
154 infomsg("listening on socket...\n");
155 if (0 > listen(sock,0)) /* accept only 1 incoming connection */
156 fatal("could not listen on socket for network input: %s\n",strerror(errno));
157 for (;;) {
158 char chost[NI_MAXHOST], serv[NI_MAXSERV];
159 struct sockaddr_in6 caddr;
160 struct addrinfo *c;
161 socklen_t len = sizeof(caddr);
162 int err;
164 debugmsg("waiting for incoming connection\n");
165 In = accept(sock, (struct sockaddr *) &caddr, &len);
166 if (0 > In)
167 fatal("Unable to accept connection for network input: %s\n",strerror(errno));
168 err = getnameinfo((struct sockaddr *) &caddr,len,chost,sizeof(chost),serv,sizeof(serv),NI_NUMERICHOST|NI_NUMERICSERV|NI_NOFQDN);
169 if (0 != err) {
170 fatal("unable to get name information for hostname of incoming connection: %s\n",gai_strerror(err));
172 infomsg("incoming connection from %s:%s\n",chost,serv);
173 if (host == 0)
174 break;
175 for (c = cinfo; c; c = c->ai_next) {
176 char xhost[NI_MAXHOST];
177 if (0 == getnameinfo((struct sockaddr *)c->ai_addr,c->ai_addrlen,xhost,sizeof(xhost),0,0,NI_NUMERICHOST|NI_NOFQDN)) {
178 debugmsg("checking against host '%s'\n",xhost);
179 if (0 == strcmp(xhost,chost))
180 break;
183 if (c)
184 break;
185 warningmsg("rejected connection from %s\n",chost);
186 if (-1 == close(In))
187 warningmsg("error closing rejected input: %s\n",strerror(errno));
189 freeaddrinfo(pinfo);
190 if (cinfo)
191 freeaddrinfo(cinfo);
192 debugmsg("input connection accepted\n");
193 setTCPBufferSize(In,SO_RCVBUF);
194 (void) close(sock);
198 dest_t *createNetworkOutput(const char *addr)
200 char *host, *port;
201 struct addrinfo hint, *ret = 0, *x;
202 int err, fd = -1;
203 dest_t *d;
205 assert(addr);
206 host = strdup(addr);
207 assert(host);
208 port = strrchr(host,':');
209 if (port == 0) {
210 fatal("syntax error - target must be given in the form <host>:<port>\n");
212 *port++ = 0;
213 bzero(&hint,sizeof(hint));
214 hint.ai_family = AddrFam;
215 hint.ai_protocol = IPPROTO_TCP;
216 hint.ai_socktype = SOCK_STREAM;
217 hint.ai_flags = AI_ADDRCONFIG;
218 debugmsg("getting address info for %s\n",addr);
219 err = getaddrinfo(host,port,&hint,&ret);
220 if (err != 0)
221 fatal("unable to resolve address information for '%s': %s\n",addr,gai_strerror(err));
222 for (x = ret; x; x = x->ai_next) {
223 fd = socket(x->ai_family, SOCK_STREAM, 0);
224 if (fd == -1) {
225 errormsg("unable to create socket: %s\n",strerror(errno));
226 continue;
228 if (0 == connect(fd, x->ai_addr, x->ai_addrlen)) {
229 debugmsg("successfully connected to %s\n",addr);
230 break;
232 (void) close(fd);
233 fd = -1;
234 warningmsg("error connecting to %s: %s\n",addr,strerror(errno));
236 if ((x == 0) || (fd == -1))
237 errormsg("unable to connect to %s\n",addr);
238 freeaddrinfo(ret);
239 if (fd != -1)
240 setTCPBufferSize(fd,SO_SNDBUF);
241 d = (dest_t *) malloc(sizeof(dest_t));
242 d->arg = addr;
243 d->name = host;
244 d->port = port;
245 d->fd = fd;
246 bzero(&d->thread,sizeof(d->thread));
247 d->result = 0;
248 d->next = 0;
249 return d;
253 #else /* HAVE_GETADDRINFO */
256 static void openNetworkInput(const char *host, unsigned short port)
258 struct sockaddr_in saddr;
259 struct hostent *h = 0, *r = 0;
260 const int reuse_addr = 1;
261 int sock;
263 debugmsg("openNetworkInput(\"%s\",%hu)\n",host,port);
264 sock = socket(AF_INET, SOCK_STREAM, 6);
265 if (0 > sock)
266 fatal("could not create socket for network input: %s\n",strerror(errno));
267 if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)))
268 warningmsg("cannot set socket to reuse address: %s\n",strerror(errno));
269 setTCPBufferSize(sock,SO_RCVBUF);
270 if (host[0]) {
271 debugmsg("resolving hostname '%s' of input...\n",host);
272 if (0 == (h = gethostbyname(host)))
273 #ifdef HAVE_HSTRERROR
274 fatal("could not resolve server hostname: %s\n",hstrerror(h_errno));
275 #else
276 fatal("could not resolve server hostname: error code %d\n",h_errno);
277 #endif
279 bzero((void *) &saddr, sizeof(saddr));
280 saddr.sin_family = AF_INET;
281 saddr.sin_addr.s_addr = htonl(INADDR_ANY);
282 saddr.sin_port = htons(port);
283 debugmsg("binding socket to port %d...\n",port);
284 if (0 > bind(sock, (struct sockaddr *) &saddr, sizeof(saddr)))
285 fatal("could not bind to socket for network input: %s\n",strerror(errno));
286 debugmsg("listening on socket...\n");
287 if (0 > listen(sock,1)) /* accept only 1 incoming connection */
288 fatal("could not listen on socket for network input: %s\n",strerror(errno));
289 for (;;) {
290 struct sockaddr_in caddr;
291 socklen_t clen = sizeof(caddr);
292 char **p;
293 debugmsg("waiting to accept connection...\n");
294 In = accept(sock, (struct sockaddr *)&caddr, &clen);
295 if (0 > In)
296 fatal("could not accept connection for network input: %s\n",strerror(errno));
297 if (host[0] == 0) {
298 infomsg("accepted connection from %s\n",inet_ntoa(caddr.sin_addr));
299 (void) close(sock);
300 return;
302 for (p = h->h_addr_list; *p; ++p) {
303 if (0 == memcmp(&caddr.sin_addr,*p,h->h_length)) {
304 infomsg("accepted connection from %s\n",inet_ntoa(caddr.sin_addr));
305 (void) close(sock);
306 return;
309 r = gethostbyaddr((char *)&caddr.sin_addr,sizeof(caddr.sin_addr.s_addr),AF_INET);
310 if (r)
311 warningmsg("rejected connection from %s (%s)\n",r->h_name,inet_ntoa(caddr.sin_addr));
312 else
313 warningmsg("rejected connection from %s\n",inet_ntoa(caddr.sin_addr));
314 if (-1 == close(In))
315 warningmsg("error closing rejected input: %s\n",strerror(errno));
320 void initNetworkInput(const char *addr)
322 char *host, *portstr;
323 unsigned pnr;
324 size_t l;
326 debugmsg("initNetworkInput(\"%s\")\n",addr);
327 l = strlen(addr) + 1;
328 host = alloca(l);
329 memcpy(host,addr,l);
330 portstr = strrchr(host,':');
331 if (portstr == 0) {
332 portstr = host;
333 host = "";
334 } else if (portstr == host) {
335 portstr = host + 1;
336 host = "";
337 *portstr = 0;
338 } else {
339 *portstr = 0;
340 ++portstr;
342 if (1 != sscanf(portstr,"%u",&pnr))
343 fatal("invalid port string '%s' - port must be given by its number, not service name\n", portstr);
344 openNetworkInput(host,pnr);
348 static void openNetworkOutput(dest_t *dest)
350 struct sockaddr_in saddr;
351 struct hostent *h = 0;
352 int out;
353 unsigned short pnr;
355 debugmsg("creating socket for output to %s:%d...\n",dest->name,dest->port);
356 if (1 != sscanf(dest->port,"%hu",&pnr))
357 fatal("port must be given by its number, not service name\n");
358 out = socket(PF_INET, SOCK_STREAM, 0);
359 if (0 > out) {
360 errormsg("could not create socket for network output: %s\n",strerror(errno));
361 return;
363 setTCPBufferSize(out,SO_SNDBUF);
364 bzero((void *) &saddr, sizeof(saddr));
365 saddr.sin_port = htons(pnr);
366 infomsg("resolving host %s...\n",dest->name);
367 if (0 == (h = gethostbyname(dest->name))) {
368 #ifdef HAVE_HSTRERROR
369 dest->result = hstrerror(h_errno);
370 errormsg("could not resolve hostname %s: %s\n",dest->name,dest->result);
371 #else
372 dest->result = "unable to resolve hostname";
373 errormsg("could not resolve hostname %s: error code %d\n",dest->name,h_errno);
374 #endif
375 dest->fd = -1;
376 (void) close(out);
377 return;
379 saddr.sin_family = h->h_addrtype;
380 assert(h->h_length <= sizeof(saddr.sin_addr));
381 (void) memcpy(&saddr.sin_addr,h->h_addr_list[0],h->h_length);
382 infomsg("connecting to server at %s...\n",inet_ntoa(saddr.sin_addr));
383 if (0 > connect(out, (struct sockaddr *) &saddr, sizeof(saddr))) {
384 dest->result = strerror(errno);
385 errormsg("could not connect to %s:%d: %s\n",dest->name,dest->port,dest->result);
386 (void) close(out);
387 out = -1;
389 dest->fd = out;
393 dest_t *createNetworkOutput(const char *addr)
395 char *host, *portstr;
396 dest_t *d = (dest_t *) malloc(sizeof(dest_t));
398 debugmsg("createNetworkOutput(\"%s\")\n",addr);
399 host = strdup(addr);
400 portstr = strrchr(host,':');
401 if ((portstr == 0) || (portstr == host))
402 fatal("argument '%s' doesn't match <host>:<port> format\n",addr);
403 *portstr++ = 0;
404 bzero(d, sizeof(dest_t));
405 d->fd = -1;
406 d->arg = addr;
407 d->name = host;
408 d->port = portstr;
409 openNetworkOutput(d);
410 return d;
414 #endif /* HAVE_GETADDRINFO */
417 /* vim:tw=0