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/>.
24 #elif defined __GNUC__
25 #define alloca __builtin_alloca
27 #define alloca __alloca
40 #include <sys/types.h>
41 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
53 int32_t TCPBufSize
= 1 << 20;
54 #if defined(PF_INET6) && defined(PF_UNSPEC)
55 int AddrFam
= PF_UNSPEC
;
57 int AddrFam
= PF_INET
;
61 static void setTCPBufferSize(int sock
, unsigned buffer
)
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
) {
73 err
= setsockopt(sock
,SOL_SOCKET
,buffer
,(void *)&size
,sizeof(size
));
75 } while ((-1 == err
) && (errno
== ENOMEM
) && (size
> osize
));
77 warningmsg("unable to set socket buffer size: %s\n",strerror(errno
));
82 err
= getsockopt(sock
,SOL_SOCKET
,buffer
,&size
,&bsize
);
84 if (buffer
== SO_RCVBUF
)
85 infomsg("set TCP receive buffer size to %d\n",size
);
87 infomsg("set TCP send buffer size to %d\n",size
);
91 #ifdef HAVE_GETADDRINFO
93 void initNetworkInput(const char *addr
)
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;
103 port
= strrchr(host
,':');
107 } else if (port
== host
) {
113 bzero(&hint
,sizeof(hint
));
114 hint
.ai_family
= AddrFam
;
115 hint
.ai_protocol
= IPPROTO_TCP
;
116 hint
.ai_socktype
= SOCK_STREAM
;
118 hint
.ai_flags
= AI_ADDRCONFIG
;
120 hint
.ai_flags
= AI_ADDRCONFIG
| AI_V4MAPPED
;
122 err
= getaddrinfo(host
,0,&hint
,&cinfo
);
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
);
133 fatal("unable to get address information for port/service '%s': %s\n",port
,gai_strerror(err
));
135 for (x
= pinfo
; x
; x
= x
->ai_next
) {
137 debugmsg("creating socket for address familiy %d\n",x
->ai_family
);
138 sock
= socket(x
->ai_family
, SOCK_STREAM
, 0);
140 warningmsg("unable to create socket for input: %s\n",strerror(errno
));
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
);
149 warningmsg("could not bind to socket for network input: %s\n",strerror(errno
));
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
));
158 char chost
[NI_MAXHOST
], serv
[NI_MAXSERV
];
159 struct sockaddr_in6 caddr
;
161 socklen_t len
= sizeof(caddr
);
164 debugmsg("waiting for incoming connection\n");
165 In
= accept(sock
, (struct sockaddr
*) &caddr
, &len
);
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
);
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
);
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
))
185 warningmsg("rejected connection from %s\n",chost
);
187 warningmsg("error closing rejected input: %s\n",strerror(errno
));
192 debugmsg("input connection accepted\n");
193 setTCPBufferSize(In
,SO_RCVBUF
);
198 dest_t
*createNetworkOutput(const char *addr
)
201 struct addrinfo hint
, *ret
= 0, *x
;
208 port
= strrchr(host
,':');
210 fatal("syntax error - target must be given in the form <host>:<port>\n");
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
);
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);
225 errormsg("unable to create socket: %s\n",strerror(errno
));
228 if (0 == connect(fd
, x
->ai_addr
, x
->ai_addrlen
)) {
229 debugmsg("successfully connected to %s\n",addr
);
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
);
240 setTCPBufferSize(fd
,SO_SNDBUF
);
241 d
= (dest_t
*) malloc(sizeof(dest_t
));
246 bzero(&d
->thread
,sizeof(d
->thread
));
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;
263 debugmsg("openNetworkInput(\"%s\",%hu)\n",host
,port
);
264 sock
= socket(AF_INET
, SOCK_STREAM
, 6);
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
);
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
));
276 fatal("could not resolve server hostname: error code %d\n",h_errno
);
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
));
290 struct sockaddr_in caddr
;
291 socklen_t clen
= sizeof(caddr
);
293 debugmsg("waiting to accept connection...\n");
294 In
= accept(sock
, (struct sockaddr
*)&caddr
, &clen
);
296 fatal("could not accept connection for network input: %s\n",strerror(errno
));
298 infomsg("accepted connection from %s\n",inet_ntoa(caddr
.sin_addr
));
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
));
309 r
= gethostbyaddr((char *)&caddr
.sin_addr
,sizeof(caddr
.sin_addr
.s_addr
),AF_INET
);
311 warningmsg("rejected connection from %s (%s)\n",r
->h_name
,inet_ntoa(caddr
.sin_addr
));
313 warningmsg("rejected connection from %s\n",inet_ntoa(caddr
.sin_addr
));
315 warningmsg("error closing rejected input: %s\n",strerror(errno
));
320 void initNetworkInput(const char *addr
)
322 char *host
, *portstr
;
326 debugmsg("initNetworkInput(\"%s\")\n",addr
);
327 l
= strlen(addr
) + 1;
330 portstr
= strrchr(host
,':');
334 } else if (portstr
== host
) {
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;
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);
360 errormsg("could not create socket for network output: %s\n",strerror(errno
));
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
);
372 dest
->result
= "unable to resolve hostname";
373 errormsg("could not resolve hostname %s: error code %d\n",dest
->name
,h_errno
);
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
);
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
);
400 portstr
= strrchr(host
,':');
401 if ((portstr
== 0) || (portstr
== host
))
402 fatal("argument '%s' doesn't match <host>:<port> format\n",addr
);
404 bzero(d
, sizeof(dest_t
));
409 openNetworkOutput(d
);
414 #endif /* HAVE_GETADDRINFO */