2 Copyright (C) Andrew Tridgell 1998
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 socket functions used in rsync
27 /* establish a proxy connection on an open socket to a web roxy by using the CONNECT
29 static int establish_proxy_connection(int fd
, char *host
, int port
)
34 slprintf(buffer
, sizeof(buffer
), "CONNECT %s:%d HTTP/1.0\r\n\r\n", host
, port
);
35 if (write(fd
, buffer
, strlen(buffer
)) != strlen(buffer
)) {
36 rprintf(FERROR
, "failed to write to proxy - %s\n",
41 for (cp
= buffer
; cp
< &buffer
[sizeof(buffer
) - 1]; cp
++) {
42 if (read(fd
, cp
, 1) != 1) {
43 rprintf(FERROR
, "failed to read from proxy\n");
55 if (strncmp(buffer
, "HTTP/", 5) != 0) {
56 rprintf(FERROR
, "bad response from proxy - %s\n",
60 for (cp
= &buffer
[5]; isdigit(*cp
) || (*cp
== '.'); cp
++)
65 rprintf(FERROR
, "bad response from proxy - %s\n",
69 /* throw away the rest of the HTTP header */
71 for (cp
= buffer
; cp
< &buffer
[sizeof(buffer
) - 1];
73 if (read(fd
, cp
, 1) != 1) {
74 rprintf(FERROR
, "failed to read from proxy\n");
80 if ((cp
> buffer
) && (*cp
== '\n'))
82 if ((cp
== buffer
) && ((*cp
== '\n') || (*cp
== '\r')))
89 /* open a socket to a tcp remote host with the specified port
90 based on code from Warren
91 proxy support by Stephen Rothwell */
92 int open_socket_out(char *host
, int port
)
94 int type
= SOCK_STREAM
;
95 struct sockaddr_in sock_out
;
104 /* if we have a RSYNC_PROXY env variable then redirect our connetcion via a web proxy
105 at the given address. The format is hostname:port */
106 h
= getenv("RSYNC_PROXY");
107 proxied
= (h
!= NULL
) && (*h
!= '\0');
110 strlcpy(buffer
, h
, sizeof(buffer
));
111 cp
= strchr(buffer
, ':');
113 rprintf(FERROR
, "invalid proxy specification\n");
124 res
= socket(PF_INET
, type
, 0);
129 hp
= gethostbyname(h
);
131 rprintf(FERROR
,"unknown host: %s\n", h
);
136 memcpy(&sock_out
.sin_addr
, hp
->h_addr
, hp
->h_length
);
137 sock_out
.sin_port
= htons(p
);
138 sock_out
.sin_family
= PF_INET
;
140 if (connect(res
,(struct sockaddr
*)&sock_out
,sizeof(sock_out
))) {
141 rprintf(FERROR
,"failed to connect to %s - %s\n", h
, strerror(errno
));
146 if (proxied
&& establish_proxy_connection(res
, host
, port
) != 0) {
151 set_nonblocking(res
);
157 /****************************************************************************
158 open a socket of the specified type, port and address for incoming data
159 ****************************************************************************/
160 static int open_socket_in(int type
, int port
, struct in_addr
*address
)
163 struct sockaddr_in sock
;
164 char host_name
[MAXHOSTNAMELEN
];
168 /* get my host name */
169 if (gethostname(host_name
, sizeof(host_name
)) == -1) {
170 rprintf(FERROR
,"gethostname failed\n");
175 if ((hp
= gethostbyname(host_name
)) == 0) {
176 rprintf(FERROR
,"gethostbyname: Unknown host %s\n",host_name
);
180 memset((char *)&sock
,0,sizeof(sock
));
181 memcpy((char *)&sock
.sin_addr
,(char *)hp
->h_addr
, hp
->h_length
);
182 sock
.sin_port
= htons(port
);
183 sock
.sin_family
= hp
->h_addrtype
;
185 sock
.sin_addr
= *address
;
187 sock
.sin_addr
.s_addr
= INADDR_ANY
;
189 res
= socket(hp
->h_addrtype
, type
, 0);
191 rprintf(FERROR
,"socket failed\n");
195 setsockopt(res
,SOL_SOCKET
,SO_REUSEADDR
,(char *)&one
,sizeof(one
));
197 /* now we've got a socket - we need to bind it */
198 if (bind(res
, (struct sockaddr
* ) &sock
,sizeof(sock
)) == -1) {
199 rprintf(FERROR
,"bind failed on port %d\n", port
);
208 /****************************************************************************
209 determine if a file descriptor is in fact a socket
210 ****************************************************************************/
211 int is_a_socket(int fd
)
215 return(getsockopt(fd
, SOL_SOCKET
, SO_TYPE
, (char *)&v
, &l
) == 0);
219 void start_accept_loop(int port
, int (*fn
)(int ))
222 extern struct in_addr socket_address
;
224 /* open an incoming socket */
225 s
= open_socket_in(SOCK_STREAM
, port
, &socket_address
);
227 exit_cleanup(RERR_SOCKETIO
);
229 /* ready to listen */
230 if (listen(s
, 5) == -1) {
232 exit_cleanup(RERR_SOCKETIO
);
236 /* now accept incoming connections - forking a new process
237 for each incoming connection */
241 struct sockaddr addr
;
242 int in_addrlen
= sizeof(addr
);
247 if (select(s
+1, &fds
, NULL
, NULL
, NULL
) != 1) {
251 if(!FD_ISSET(s
, &fds
)) continue;
253 fd
= accept(s
,&addr
,&in_addrlen
);
255 if (fd
== -1) continue;
257 signal(SIGCHLD
, SIG_IGN
);
259 /* we shouldn't have any children left hanging around
260 but I have had reports that on Digital Unix zombies
261 are produced, so this ensures that they are reaped */
263 while (waitpid(-1, NULL
, WNOHANG
) > 0);
279 enum SOCK_OPT_TYPES
{OPT_BOOL
,OPT_INT
,OPT_ON
};
288 } socket_options
[] = {
289 {"SO_KEEPALIVE", SOL_SOCKET
, SO_KEEPALIVE
, 0, OPT_BOOL
},
290 {"SO_REUSEADDR", SOL_SOCKET
, SO_REUSEADDR
, 0, OPT_BOOL
},
291 {"SO_BROADCAST", SOL_SOCKET
, SO_BROADCAST
, 0, OPT_BOOL
},
293 {"TCP_NODELAY", IPPROTO_TCP
, TCP_NODELAY
, 0, OPT_BOOL
},
295 #ifdef IPTOS_LOWDELAY
296 {"IPTOS_LOWDELAY", IPPROTO_IP
, IP_TOS
, IPTOS_LOWDELAY
, OPT_ON
},
298 #ifdef IPTOS_THROUGHPUT
299 {"IPTOS_THROUGHPUT", IPPROTO_IP
, IP_TOS
, IPTOS_THROUGHPUT
, OPT_ON
},
302 {"SO_SNDBUF", SOL_SOCKET
, SO_SNDBUF
, 0, OPT_INT
},
305 {"SO_RCVBUF", SOL_SOCKET
, SO_RCVBUF
, 0, OPT_INT
},
308 {"SO_SNDLOWAT", SOL_SOCKET
, SO_SNDLOWAT
, 0, OPT_INT
},
311 {"SO_RCVLOWAT", SOL_SOCKET
, SO_RCVLOWAT
, 0, OPT_INT
},
314 {"SO_SNDTIMEO", SOL_SOCKET
, SO_SNDTIMEO
, 0, OPT_INT
},
317 {"SO_RCVTIMEO", SOL_SOCKET
, SO_RCVTIMEO
, 0, OPT_INT
},
323 /****************************************************************************
324 set user socket options
325 ****************************************************************************/
326 void set_socket_options(int fd
, char *options
)
329 if (!options
|| !*options
) return;
331 options
= strdup(options
);
333 if (!options
) out_of_memory("set_socket_options");
335 for (tok
=strtok(options
, " \t,"); tok
; tok
=strtok(NULL
," \t,")) {
341 if ((p
= strchr(tok
,'='))) {
347 for (i
=0;socket_options
[i
].name
;i
++)
348 if (strcmp(socket_options
[i
].name
,tok
)==0)
351 if (!socket_options
[i
].name
) {
352 rprintf(FERROR
,"Unknown socket option %s\n",tok
);
356 switch (socket_options
[i
].opttype
) {
359 ret
= setsockopt(fd
,socket_options
[i
].level
,
360 socket_options
[i
].option
,(char *)&value
,sizeof(int));
365 rprintf(FERROR
,"syntax error - %s does not take a value\n",tok
);
368 int on
= socket_options
[i
].value
;
369 ret
= setsockopt(fd
,socket_options
[i
].level
,
370 socket_options
[i
].option
,(char *)&on
,sizeof(int));
376 rprintf(FERROR
,"Failed to set socket option %s\n",tok
);
382 /****************************************************************************
383 become a daemon, discarding the controlling terminal
384 ****************************************************************************/
385 void become_daemon(void)
393 /* detach from the terminal */
398 i
= open("/dev/tty", O_RDWR
);
400 ioctl(i
, (int) TIOCNOTTY
, (char *)0);
403 #endif /* TIOCNOTTY */
405 /* make sure that stdin, stdout an stderr don't stuff things
406 up (library functions, for example) */
409 open("/dev/null", O_RDWR
);
413 /*******************************************************************
414 return the IP addr of the client as a string
415 ******************************************************************/
416 char *client_addr(int fd
)
419 struct sockaddr_in
*sockin
= (struct sockaddr_in
*) (&sa
);
420 int length
= sizeof(sa
);
421 static char addr_buf
[100];
422 static int initialised
;
424 if (initialised
) return addr_buf
;
428 if (getpeername(fd
, &sa
, &length
)) {
429 exit_cleanup(RERR_SOCKETIO
);
432 strlcpy(addr_buf
,(char *)inet_ntoa(sockin
->sin_addr
), sizeof(addr_buf
));
437 /*******************************************************************
438 return the DNS name of the client
439 ******************************************************************/
440 char *client_name(int fd
)
443 struct sockaddr_in
*sockin
= (struct sockaddr_in
*) (&sa
);
444 int length
= sizeof(sa
);
445 static char name_buf
[100];
448 char *def
= "UNKNOWN";
449 static int initialised
;
451 if (initialised
) return name_buf
;
455 strcpy(name_buf
,def
);
457 if (getpeername(fd
, &sa
, &length
)) {
458 exit_cleanup(RERR_SOCKETIO
);
461 /* Look up the remote host name. */
462 if ((hp
= gethostbyaddr((char *) &sockin
->sin_addr
,
463 sizeof(sockin
->sin_addr
),
465 strlcpy(name_buf
,(char *)hp
->h_name
,sizeof(name_buf
));
469 /* do a forward lookup as well to prevent spoofing */
470 hp
= gethostbyname(name_buf
);
472 strcpy(name_buf
,def
);
473 rprintf(FERROR
,"reverse name lookup failed\n");
475 for (p
=hp
->h_addr_list
;*p
;p
++) {
476 if (memcmp(*p
, &sockin
->sin_addr
, hp
->h_length
) == 0) {
481 strcpy(name_buf
,def
);
482 rprintf(FERROR
,"reverse name lookup mismatch - spoofed address?\n");
489 /*******************************************************************
490 convert a string to an IP address. The string can be a name or
491 dotted decimal number
492 ******************************************************************/
493 struct in_addr
*ip_address(const char *str
)
495 static struct in_addr ret
;
498 /* try as an IP address */
499 if (inet_aton(str
, &ret
) != 0) {
503 /* otherwise assume it's a network name of some sort and use
505 if ((hp
= gethostbyname(str
)) == 0) {
506 rprintf(FERROR
, "gethostbyname: Unknown host. %s\n",str
);
510 if (hp
->h_addr
== NULL
) {
511 rprintf(FERROR
, "gethostbyname: host address is invalid for host %s\n",str
);
515 if (hp
->h_length
> sizeof(ret
)) {
516 rprintf(FERROR
, "gethostbyname: host address is too large\n");
520 memcpy(&ret
.s_addr
, hp
->h_addr
, hp
->h_length
);