X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source%2Flib%2Futil_sock.c;h=1508ddfce34ec0a733ec06a105e928f1afbd6408;hb=5c6c8e1fe93;hp=2acb20861b966fa68af1b80d081ad2b7ce28a3f6;hpb=f755711df8f74f9b8e8c1a2b0d07d02a931eeb89;p=samba.git diff --git a/source/lib/util_sock.c b/source/lib/util_sock.c index 2acb20861b9..1508ddfce34 100644 --- a/source/lib/util_sock.c +++ b/source/lib/util_sock.c @@ -1,31 +1,140 @@ -/* +/* Unix SMB/CIFS implementation. Samba utility functions Copyright (C) Andrew Tridgell 1992-1998 Copyright (C) Tim Potter 2000-2001 - + Copyright (C) Jeremy Allison 1992-2007 + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. If not, see . */ #include "includes.h" -/* the last IP received from */ -struct in_addr lastip; +/* the following 3 client_*() functions are nasty ways of allowing + some generic functions to get info that really should be hidden in + particular modules */ +static int client_fd = -1; +/* What to print out on a client disconnect error. */ +static char client_ip_string[INET6_ADDRSTRLEN]; + +/**************************************************************************** + Pritn out an IPv4 or IPv6 address from a struct sockaddr_storage. +****************************************************************************/ + +char *print_sockaddr(char *dest, + size_t destlen, + struct sockaddr_storage *psa) +{ + if (destlen > 0) { + dest[0] = '\0'; + } +#ifdef AF_INET6 + if (psa->ss_family == AF_INET6) { + inet_ntop(AF_INET6, + &((struct sockaddr_in6 *)psa)->sin6_addr, + dest, + destlen); + } +#endif + if (psa->ss_family == AF_INET) { + inet_ntop(AF_INET, + &((struct sockaddr_in *)psa)->sin_addr, + dest, + destlen); + } + return dest; +} + +void client_setfd(int fd) +{ + client_fd = fd; + safe_strcpy(client_ip_string, + get_peer_addr(client_fd), + sizeof(client_ip_string)-1); +} + +static char *get_socket_addr(int fd) +{ + struct sockaddr_storage sa; + socklen_t length = sizeof(sa); + static char addr_buf[INET6_ADDRSTRLEN]; + + /* Ok, returning a hard coded IPv4 address + * is bogus, but it's just as bogus as a + * zero IPv6 address. No good choice here. + */ + + safe_strcpy(addr_buf, "0.0.0.0", sizeof(addr_buf)-1); + + if (fd == -1) { + return addr_buf; + } + + if (getsockname(fd, (struct sockaddr *)&sa, &length) < 0) { + DEBUG(0,("getsockname failed. Error was %s\n", + strerror(errno) )); + return addr_buf; + } + + return print_sockaddr(addr_buf, sizeof(addr_buf), &sa); +} + +static int get_socket_port(int fd) +{ + struct sockaddr_storage sa; + socklen_t length = sizeof(sa); + + if (fd == -1) { + return -1; + } + + if (getsockname(fd, (struct sockaddr *)&sa, &length) < 0) { + DEBUG(0,("getpeername failed. Error was %s\n", + strerror(errno) )); + return -1; + } + +#ifdef AF_INET6 + if (sa.ss_family == AF_INET6) { + return ntohs(((struct sockaddr_in6 *)&sa)->sin6_port); + } +#endif + if (sa.ss_family == AF_INET) { + return ntohs(((struct sockaddr_in *)&sa)->sin_port); + } + return -1; +} -/* the last port received from */ -int lastport=0; +char *client_name(void) +{ + return get_peer_name(client_fd,False); +} + +char *client_addr(void) +{ + return get_peer_addr(client_fd); +} + +char *client_socket_addr(void) +{ + return get_socket_addr(client_fd); +} + +int client_socket_port(void) +{ + return get_socket_port(client_fd); +} int smb_read_error = 0; @@ -35,7 +144,8 @@ int smb_read_error = 0; BOOL is_a_socket(int fd) { - int v,l; + int v; + socklen_t l; l = sizeof(int); return(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0); } @@ -43,7 +153,7 @@ BOOL is_a_socket(int fd) enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON}; typedef struct smb_socket_option { - char *name; + const char *name; int level; int option; int value; @@ -51,38 +161,50 @@ typedef struct smb_socket_option { } smb_socket_option; static const smb_socket_option socket_options[] = { - {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL}, - {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL}, - {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL}, + {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL}, + {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL}, + {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL}, #ifdef TCP_NODELAY - {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL}, + {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL}, +#endif +#ifdef TCP_KEEPCNT + {"TCP_KEEPCNT", IPPROTO_TCP, TCP_KEEPCNT, 0, OPT_INT}, +#endif +#ifdef TCP_KEEPIDLE + {"TCP_KEEPIDLE", IPPROTO_TCP, TCP_KEEPIDLE, 0, OPT_INT}, +#endif +#ifdef TCP_KEEPINTVL + {"TCP_KEEPINTVL", IPPROTO_TCP, TCP_KEEPINTVL, 0, OPT_INT}, #endif #ifdef IPTOS_LOWDELAY - {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON}, + {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON}, #endif #ifdef IPTOS_THROUGHPUT - {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON}, + {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON}, #endif #ifdef SO_REUSEPORT - {"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL}, + {"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL}, #endif #ifdef SO_SNDBUF - {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT}, + {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT}, #endif #ifdef SO_RCVBUF - {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT}, + {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT}, #endif #ifdef SO_SNDLOWAT - {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT}, + {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT}, #endif #ifdef SO_RCVLOWAT - {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT}, + {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT}, #endif #ifdef SO_SNDTIMEO - {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT}, + {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT}, #endif #ifdef SO_RCVTIMEO - {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT}, + {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT}, +#endif +#ifdef TCP_FASTACK + {"TCP_FASTACK", IPPROTO_TCP, TCP_FASTACK, 0, OPT_INT}, #endif {NULL,0,0,0,0}}; @@ -92,14 +214,23 @@ static const smb_socket_option socket_options[] = { static void print_socket_options(int s) { - int value, vlen = 4; + int value; + socklen_t vlen = 4; const smb_socket_option *p = &socket_options[0]; - for (; p->name != NULL; p++) { - if (getsockopt(s, p->level, p->option, (void *)&value, &vlen) == -1) { - DEBUG(5,("Could not test socket option %s.\n", p->name)); - } else { - DEBUG(5,("socket option %s = %d\n",p->name,value)); + /* wrapped in if statement to prevent streams + * leak in SCO Openserver 5.0 */ + /* reported on samba-technical --jerry */ + if ( DEBUGLEVEL >= 5 ) { + for (; p->name != NULL; p++) { + if (getsockopt(s, p->level, p->option, + (void *)&value, &vlen) == -1) { + DEBUG(5,("Could not test socket option %s.\n", + p->name)); + } else { + DEBUG(5,("socket option %s = %d\n", + p->name,value)); + } } } } @@ -137,23 +268,28 @@ void set_socket_options(int fd, const char *options) case OPT_BOOL: case OPT_INT: ret = setsockopt(fd,socket_options[i].level, - socket_options[i].option,(char *)&value,sizeof(int)); + socket_options[i].option, + (char *)&value,sizeof(int)); break; case OPT_ON: if (got_value) - DEBUG(0,("syntax error - %s does not take a value\n",tok)); + DEBUG(0,("syntax error - %s " + "does not take a value\n",tok)); { int on = socket_options[i].value; ret = setsockopt(fd,socket_options[i].level, - socket_options[i].option,(char *)&on,sizeof(int)); + socket_options[i].option, + (char *)&on,sizeof(int)); } - break; + break; + } + + if (ret != 0) { + DEBUG(0,("Failed to set socket option %s (Error %s)\n", + tok, strerror(errno) )); } - - if (ret != 0) - DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) )); } print_socket_options(fd); @@ -163,51 +299,42 @@ void set_socket_options(int fd, const char *options) Read from a socket. ****************************************************************************/ -ssize_t read_udp_socket(int fd,char *buf,size_t len) +ssize_t read_udp_v4_socket(int fd, + char *buf, + size_t len, + struct sockaddr_storage *psa) { ssize_t ret; - struct sockaddr_in sock; - socklen_t socklen = sizeof(sock); + socklen_t socklen = sizeof(*psa); + struct sockaddr_in *si = (struct sockaddr_in *)psa; + + memset((char *)psa,'\0',socklen); - memset((char *)&sock,'\0',socklen); - memset((char *)&lastip,'\0',sizeof(lastip)); - ret = (ssize_t)sys_recvfrom(fd,buf,len,0,(struct sockaddr *)&sock,&socklen); + ret = (ssize_t)sys_recvfrom(fd,buf,len,0, + (struct sockaddr *)psa,&socklen); if (ret <= 0) { - DEBUG(2,("read socket failed. ERRNO=%s\n",strerror(errno))); - return(0); + /* Don't print a low debug error for a non-blocking socket. */ + if (errno == EAGAIN) { + DEBUG(10,("read_udp_v4_socket: returned EAGAIN\n")); + } else { + DEBUG(2,("read_udp_v4_socket: failed. errno=%s\n", + strerror(errno))); + } + return 0; } - lastip = sock.sin_addr; - lastport = ntohs(sock.sin_port); - - DEBUG(10,("read_udp_socket: lastip %s lastport %d read: %d\n", - inet_ntoa(lastip), lastport, ret)); - - return(ret); -} - -/******************************************************************* - checks if read data is outstanding. - ********************************************************************/ -static int read_data_outstanding(int fd, unsigned int time_out) -{ - int selrtn; - fd_set fds; - struct timeval timeout; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - timeout.tv_sec = (time_t) (time_out / 1000); - timeout.tv_usec = (long)(1000 * (time_out % 1000)); + if (psa->ss_family != AF_INET) { + DEBUG(2,("read_udp_v4_socket: invalid address family %d " + "(not IPv4)\n", (int)psa->ss_family)); + return 0; + } - selrtn = sys_select_intr(fd + 1, &fds, NULL, NULL, &timeout); + DEBUG(10,("read_udp_v4_socket: ip %s port %d read: %lu\n", + inet_ntoa(si->sin_addr), + si->sin_port, + (unsigned long)ret)); - if (selrtn <= 0) - { - return selrtn; - } - return FD_ISSET(fd, &fds) ? 1 : 0; + return ret; } /**************************************************************************** @@ -217,35 +344,53 @@ static int read_data_outstanding(int fd, unsigned int time_out) time_out = timeout in milliseconds ****************************************************************************/ -static ssize_t read_socket_with_timeout(int fd,char *buf,size_t mincnt,size_t maxcnt,unsigned int time_out) +ssize_t read_socket_with_timeout(int fd, + char *buf, + size_t mincnt, + size_t maxcnt, + unsigned int time_out) { fd_set fds; int selrtn; ssize_t readret; size_t nread = 0; struct timeval timeout; - + /* just checking .... */ if (maxcnt <= 0) return(0); - + smb_read_error = 0; - + /* Blocking read */ - if (time_out <= 0) { - if (mincnt == 0) mincnt = maxcnt; - + if (time_out == 0) { + if (mincnt == 0) { + mincnt = maxcnt; + } + while (nread < mincnt) { readret = sys_read(fd, buf + nread, maxcnt - nread); - + if (readret == 0) { - DEBUG(5,("read_socket_with_timeout: blocking read. EOF from client.\n")); + DEBUG(5,("read_socket_with_timeout: " + "blocking read. EOF from client.\n")); smb_read_error = READ_EOF; return -1; } - + if (readret == -1) { - DEBUG(0,("read_socket_with_timeout: read error = %s.\n", strerror(errno) )); + if (fd == client_fd) { + /* Try and give an error message + * saying what client failed. */ + DEBUG(0,("read_socket_with_timeout: " + "client %s read error = %s.\n", + client_ip_string, + strerror(errno) )); + } else { + DEBUG(0,("read_socket_with_timeout: " + "read error = %s.\n", + strerror(errno) )); + } smb_read_error = READ_ERROR; return -1; } @@ -253,169 +398,120 @@ static ssize_t read_socket_with_timeout(int fd,char *buf,size_t mincnt,size_t ma } return((ssize_t)nread); } - + /* Most difficult - timeout read */ - /* If this is ever called on a disk file and + /* If this is ever called on a disk file and mincnt is greater then the filesize then - system performance will suffer severely as + system performance will suffer severely as select always returns true on disk files */ - + /* Set initial timeout */ timeout.tv_sec = (time_t)(time_out / 1000); timeout.tv_usec = (long)(1000 * (time_out % 1000)); - - for (nread=0; nread < mincnt; ) { + + for (nread=0; nread < mincnt; ) { FD_ZERO(&fds); FD_SET(fd,&fds); - + selrtn = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout); - + /* Check if error */ if (selrtn == -1) { /* something is wrong. Maybe the socket is dead? */ - DEBUG(0,("read_socket_with_timeout: timeout read. select error = %s.\n", strerror(errno) )); + if (fd == client_fd) { + /* Try and give an error message saying + * what client failed. */ + DEBUG(0,("read_socket_with_timeout: timeout " + "read for client %s. select error = %s.\n", + client_ip_string, strerror(errno) )); + } else { + DEBUG(0,("read_socket_with_timeout: timeout " + "read. select error = %s.\n", + strerror(errno) )); + } smb_read_error = READ_ERROR; return -1; } - + /* Did we timeout ? */ if (selrtn == 0) { - DEBUG(10,("read_socket_with_timeout: timeout read. select timed out.\n")); + DEBUG(10,("read_socket_with_timeout: timeout read. " + "select timed out.\n")); smb_read_error = READ_TIMEOUT; return -1; } - + readret = sys_read(fd, buf+nread, maxcnt-nread); - + if (readret == 0) { /* we got EOF on the file descriptor */ - DEBUG(5,("read_socket_with_timeout: timeout read. EOF from client.\n")); + DEBUG(5,("read_socket_with_timeout: timeout read. " + "EOF from client.\n")); smb_read_error = READ_EOF; return -1; } - + if (readret == -1) { /* the descriptor is probably dead */ - DEBUG(0,("read_socket_with_timeout: timeout read. read error = %s.\n", strerror(errno) )); + if (fd == client_fd) { + /* Try and give an error message + * saying what client failed. */ + DEBUG(0,("read_socket_with_timeout: timeout " + "read to client %s. read error = %s.\n", + client_ip_string, strerror(errno) )); + } else { + DEBUG(0,("read_socket_with_timeout: timeout " + "read. read error = %s.\n", + strerror(errno) )); + } smb_read_error = READ_ERROR; return -1; } - - nread += readret; - } - - /* Return the number we got */ - return (ssize_t)nread; -} -/**************************************************************************** - Read data from a fd with a timout in msec. - mincount = if timeout, minimum to read before returning - maxcount = number to be read. - time_out = timeout in milliseconds -****************************************************************************/ - -ssize_t read_with_timeout(int fd, char *buf, size_t mincnt, size_t maxcnt, - unsigned int time_out) -{ - ssize_t readret; - size_t nread = 0; - - /* just checking .... */ - if (maxcnt <= 0) - return(0); - - /* Blocking read */ - if (time_out <= 0) { - if (mincnt == 0) mincnt = maxcnt; - - while (nread < mincnt) { - readret = sys_read(fd, buf + nread, maxcnt - nread); - - if (readret <= 0) - return readret; - - nread += readret; - } - return((ssize_t)nread); - } - - /* Most difficult - timeout read */ - /* If this is ever called on a disk file and - mincnt is greater then the filesize then - system performance will suffer severely as - select always returns true on disk files */ - - for (nread=0; nread < mincnt; ) { - int selrtn = read_data_outstanding(fd, time_out); - - if(selrtn <= 0) - return selrtn; - - readret = sys_read(fd, buf+nread, maxcnt-nread); - - if (readret <= 0) - return readret; - nread += readret; } - + /* Return the number we got */ - return((ssize_t)nread); + return (ssize_t)nread; } /**************************************************************************** - read data from the client, reading exactly N bytes. + Read data from the client, reading exactly N bytes. ****************************************************************************/ ssize_t read_data(int fd,char *buffer,size_t N) { ssize_t ret; - size_t total=0; - - smb_read_error = 0; - - while (total < N) { - ret = sys_read(fd,buffer + total,N - total); - - if (ret == 0) { - DEBUG(10,("read_data: read of %d returned 0. Error = %s\n", (int)(N - total), strerror(errno) )); - smb_read_error = READ_EOF; - return 0; - } - - if (ret == -1) { - DEBUG(0,("read_data: read failure for %d. Error = %s\n", (int)(N - total), strerror(errno) )); - smb_read_error = READ_ERROR; - return -1; - } - total += ret; - } - return (ssize_t)total; -} - -/**************************************************************************** - Read data from a socket, reading exactly N bytes. -****************************************************************************/ + size_t total=0; -static ssize_t read_socket_data(int fd,char *buffer,size_t N) -{ - ssize_t ret; - size_t total=0; - smb_read_error = 0; while (total < N) { ret = sys_read(fd,buffer + total,N - total); if (ret == 0) { - DEBUG(10,("read_socket_data: recv of %d returned 0. Error = %s\n", (int)(N - total), strerror(errno) )); + DEBUG(10,("read_data: read of %d returned 0. " + "Error = %s\n", + (int)(N - total), strerror(errno) )); smb_read_error = READ_EOF; return 0; } if (ret == -1) { - DEBUG(0,("read_socket_data: recv failure for %d. Error = %s\n", (int)(N - total), strerror(errno) )); + if (fd == client_fd) { + /* Try and give an error message saying + * what client failed. */ + DEBUG(0,("read_data: read failure for %d " + "bytes to client %s. Error = %s\n", + (int)(N - total), + client_ip_string, + strerror(errno) )); + } else { + DEBUG(0,("read_data: read failure for %d. " + "Error = %s\n", + (int)(N - total), + strerror(errno) )); + } smb_read_error = READ_ERROR; return -1; } @@ -428,7 +524,7 @@ static ssize_t read_socket_data(int fd,char *buffer,size_t N) Write data to a fd. ****************************************************************************/ -ssize_t write_data(int fd,char *buffer,size_t N) +ssize_t write_data(int fd, const char *buffer, size_t N) { size_t total=0; ssize_t ret; @@ -437,35 +533,22 @@ ssize_t write_data(int fd,char *buffer,size_t N) ret = sys_write(fd,buffer + total,N - total); if (ret == -1) { - DEBUG(0,("write_data: write failure. Error = %s\n", strerror(errno) )); + if (fd == client_fd) { + /* Try and give an error message saying + * what client failed. */ + DEBUG(0,("write_data: write failure in " + "writing to client %s. Error %s\n", + client_ip_string, strerror(errno) )); + } else { + DEBUG(0,("write_data: write failure. " + "Error = %s\n", strerror(errno) )); + } return -1; } - if (ret == 0) - return total; - - total += ret; - } - return (ssize_t)total; -} - -/**************************************************************************** - Write data to a socket - use send rather than write. -****************************************************************************/ -static ssize_t write_socket_data(int fd,char *buffer,size_t N) -{ - size_t total=0; - ssize_t ret; - - while (total < N) { - ret = sys_send(fd,buffer + total,N - total,0); - - if (ret == -1) { - DEBUG(0,("write_socket_data: write failure. Error = %s\n", strerror(errno) )); - return -1; - } - if (ret == 0) + if (ret == 0) { return total; + } total += ret; } @@ -473,26 +556,7 @@ static ssize_t write_socket_data(int fd,char *buffer,size_t N) } /**************************************************************************** -write to a socket -****************************************************************************/ - -ssize_t write_socket(int fd,char *buf,size_t len) -{ - ssize_t ret=0; - - DEBUG(6,("write_socket(%d,%d)\n",fd,(int)len)); - ret = write_socket_data(fd,buf,len); - - DEBUG(6,("write_socket(%d,%d) wrote %d\n",fd,(int)len,(int)ret)); - if(ret <= 0) - DEBUG(0,("write_socket: Error writing %d bytes to socket %d: ERRNO = %s\n", - (int)len, fd, strerror(errno) )); - - return(ret); -} - -/**************************************************************************** -send a keepalive packet (rfc1002) + Send a keepalive packet (rfc1002). ****************************************************************************/ BOOL send_keepalive(int client) @@ -502,53 +566,57 @@ BOOL send_keepalive(int client) buf[0] = SMBkeepalive; buf[1] = buf[2] = buf[3] = 0; - return(write_socket_data(client,(char *)buf,4) == 4); + return(write_data(client,(char *)buf,4) == 4); } - /**************************************************************************** -read 4 bytes of a smb packet and return the smb length of the packet -store the result in the buffer -This version of the function will return a length of zero on receiving -a keepalive packet. -timeout is in milliseconds. + Read 4 bytes of a smb packet and return the smb length of the packet. + Store the result in the buffer. + This version of the function will return a length of zero on receiving + a keepalive packet. + Timeout is in milliseconds. ****************************************************************************/ -static ssize_t read_smb_length_return_keepalive(int fd,char *inbuf,unsigned int timeout) +static ssize_t read_smb_length_return_keepalive(int fd, + char *inbuf, + unsigned int timeout) { ssize_t len=0; int msg_type; BOOL ok = False; while (!ok) { - if (timeout > 0) - ok = (read_socket_with_timeout(fd,inbuf,4,4,timeout) == 4); - else - ok = (read_socket_data(fd,inbuf,4) == 4); - - if (!ok) - return(-1); + if (timeout > 0) { + ok = (read_socket_with_timeout(fd,inbuf,4,4,timeout) + == 4); + } else { + ok = (read_data(fd,inbuf,4) == 4); + } + if (!ok) { + return -1; + } len = smb_len(inbuf); msg_type = CVAL(inbuf,0); - if (msg_type == SMBkeepalive) + if (msg_type == SMBkeepalive) { DEBUG(5,("Got keepalive packet\n")); + } } - DEBUG(10,("got smb length of %d\n",len)); + DEBUG(10,("got smb length of %lu\n",(unsigned long)len)); return(len); } /**************************************************************************** -read 4 bytes of a smb packet and return the smb length of the packet -store the result in the buffer. This version of the function will -never return a session keepalive (length of zero). -timeout is in milliseconds. + Read 4 bytes of a smb packet and return the smb length of the packet. + Store the result in the buffer. This version of the function will + never return a session keepalive (length of zero). + Timeout is in milliseconds. ****************************************************************************/ -ssize_t read_smb_length(int fd,char *inbuf,unsigned int timeout) +ssize_t read_smb_length(int fd, char *inbuf, unsigned int timeout) { ssize_t len; @@ -563,30 +631,34 @@ ssize_t read_smb_length(int fd,char *inbuf,unsigned int timeout) break; } - DEBUG(10,("read_smb_length: got smb length of %d\n",len)); + DEBUG(10,("read_smb_length: got smb length of %lu\n", + (unsigned long)len)); return len; } /**************************************************************************** - read an smb from a fd. Note that the buffer *MUST* be of size - BUFFER_SIZE+SAFETY_MARGIN. - The timeout is in milliseconds. - This function will return on a - receipt of a session keepalive packet. + Read an smb from a fd. Note that the buffer *MUST* be of size + BUFFER_SIZE+SAFETY_MARGIN. + The timeout is in milliseconds. + This function will return on receipt of a session keepalive packet. + maxlen is the max number of bytes to return, not including the 4 byte + length. If zero it means BUFFER_SIZE+SAFETY_MARGIN limit. + Doesn't check the MAC on signed packets. ****************************************************************************/ -BOOL receive_smb(int fd,char *buffer, unsigned int timeout) +ssize_t receive_smb_raw(int fd, + char *buffer, + unsigned int timeout, + size_t maxlen) { ssize_t len,ret; smb_read_error = 0; - memset(buffer,'\0',smb_size + 100); - len = read_smb_length_return_keepalive(fd,buffer,timeout); if (len < 0) { - DEBUG(10,("receive_smb: length < 0!\n")); + DEBUG(10,("receive_smb_raw: length < 0!\n")); /* * Correct fix. smb_read_error may have already been @@ -596,7 +668,7 @@ BOOL receive_smb(int fd,char *buffer, unsigned int timeout) if (smb_read_error == 0) smb_read_error = READ_ERROR; - return False; + return -1; } /* @@ -605,7 +677,8 @@ BOOL receive_smb(int fd,char *buffer, unsigned int timeout) */ if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) { - DEBUG(0,("Invalid packet length! (%d bytes).\n",len)); + DEBUG(0,("Invalid packet length! (%lu bytes).\n", + (unsigned long)len)); if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) { /* @@ -616,35 +689,186 @@ BOOL receive_smb(int fd,char *buffer, unsigned int timeout) if (smb_read_error == 0) smb_read_error = READ_ERROR; - return False; + return -1; } } if(len > 0) { - ret = read_socket_data(fd,buffer+4,len); + if (maxlen) { + len = MIN(len,maxlen); + } + + if (timeout > 0) { + ret = read_socket_with_timeout(fd, + buffer+4, + len, + len, + timeout); + } else { + ret = read_data(fd,buffer+4,len); + } + if (ret != len) { + if (smb_read_error == 0) { + smb_read_error = READ_ERROR; + } + return -1; + } + + /* not all of samba3 properly checks for packet-termination + * of strings. This ensures that we don't run off into + * empty space. */ + SSVAL(buffer+4,len, 0); + } + + return len; +} + +static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd, + char **buffer, unsigned int timeout) +{ + char lenbuf[4]; + ssize_t len,ret; + + smb_read_error = 0; + + len = read_smb_length_return_keepalive(fd, lenbuf, timeout); + if (len < 0) { + DEBUG(10,("receive_smb_raw: length < 0!\n")); + + /* + * Correct fix. smb_read_error may have already been + * set. Only set it here if not already set. Global + * variables still suck :-). JRA. + */ + + if (smb_read_error == 0) + smb_read_error = READ_ERROR; + return -1; + } + + /* + * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes + * of header. Don't print the error if this fits.... JRA. + */ + + if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) { + DEBUG(0,("Invalid packet length! (%lu bytes).\n", + (unsigned long)len)); + if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) { + + /* + * Correct fix. smb_read_error may have already been + * set. Only set it here if not already set. Global + * variables still suck :-). JRA. + */ + if (smb_read_error == 0) smb_read_error = READ_ERROR; - return False; + return -1; + } + } + + /* + * The +4 here can't wrap, we've checked the length above already. + */ + + *buffer = TALLOC_ARRAY(mem_ctx, char, len+4); + + if (*buffer == NULL) { + DEBUG(0, ("Could not allocate inbuf of length %d\n", + (int)len+4)); + if (smb_read_error == 0) + smb_read_error = READ_ERROR; + return -1; + } + + memcpy(*buffer, lenbuf, sizeof(lenbuf)); + + if(len > 0) { + if (timeout > 0) { + ret = read_socket_with_timeout(fd,(*buffer)+4, len, + len, timeout); + } else { + ret = read_data(fd, (*buffer)+4, len); + } + + if (ret != len) { + if (smb_read_error == 0) { + smb_read_error = READ_ERROR; + } + return -1; } } - return(True); + return len + 4; } /**************************************************************************** - send an smb to a fd + Wrapper for receive_smb_raw(). + Checks the MAC on signed packets. ****************************************************************************/ -BOOL send_smb(int fd,char *buffer) +BOOL receive_smb(int fd, char *buffer, unsigned int timeout) +{ + if (receive_smb_raw(fd, buffer, timeout, 0) < 0) { + return False; + } + + /* Check the incoming SMB signature. */ + if (!srv_check_sign_mac(buffer, True)) { + DEBUG(0, ("receive_smb: SMB Signature verification " + "failed on incoming packet!\n")); + if (smb_read_error == 0) { + smb_read_error = READ_BAD_SIG; + } + return False; + } + + return True; +} + +ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer, + unsigned int timeout) +{ + ssize_t len; + + len = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout); + + if (len < 0) { + return -1; + } + + /* Check the incoming SMB signature. */ + if (!srv_check_sign_mac(*buffer, True)) { + DEBUG(0, ("receive_smb: SMB Signature verification failed on " + "incoming packet!\n")); + if (smb_read_error == 0) { + smb_read_error = READ_BAD_SIG; + } + return -1; + } + + return len; +} + +/**************************************************************************** + Send an smb to a fd. +****************************************************************************/ + +BOOL send_smb(int fd, char *buffer) { size_t len; size_t nwritten=0; ssize_t ret; + + /* Sign the outgoing packet if required. */ + srv_calculate_sign_mac(buffer); + len = smb_len(buffer) + 4; while (nwritten < len) { - ret = write_socket(fd,buffer+nwritten,len - nwritten); + ret = write_data(fd,buffer+nwritten,len - nwritten); if (ret <= 0) { DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n", (int)len,(int)ret, strerror(errno) )); @@ -660,7 +884,11 @@ BOOL send_smb(int fd,char *buffer) Open a socket of the specified type, port, and address for incoming data. ****************************************************************************/ -int open_socket_in( int type, int port, int dlevel, uint32 socket_addr, BOOL rebind ) +int open_socket_in(int type, + int port, + int dlevel, + uint32 socket_addr, + BOOL rebind ) { struct sockaddr_in sock; int res; @@ -686,19 +914,23 @@ int open_socket_in( int type, int port, int dlevel, uint32 socket_addr, BOOL reb /* This block sets/clears the SO_REUSEADDR and possibly SO_REUSEPORT. */ { int val = rebind ? 1 : 0; - if( setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) == -1 ) { + if( setsockopt(res,SOL_SOCKET,SO_REUSEADDR, + (char *)&val,sizeof(val)) == -1 ) { if( DEBUGLVL( dlevel ) ) { dbgtext( "open_socket_in(): setsockopt: " ); - dbgtext( "SO_REUSEADDR = %s ", val?"True":"False" ); + dbgtext( "SO_REUSEADDR = %s ", + val?"True":"False" ); dbgtext( "on port %d failed ", port ); dbgtext( "with error = %s\n", strerror(errno) ); } } #ifdef SO_REUSEPORT - if( setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)) == -1 ) { + if( setsockopt(res,SOL_SOCKET,SO_REUSEPORT, + (char *)&val,sizeof(val)) == -1 ) { if( DEBUGLVL( dlevel ) ) { dbgtext( "open_socket_in(): setsockopt: "); - dbgtext( "SO_REUSEPORT = %s ", val?"True":"False" ); + dbgtext( "SO_REUSEPORT = %s ", + val?"True":"False" ); dbgtext( "on port %d failed ", port ); dbgtext( "with error = %s\n", strerror(errno) ); } @@ -708,13 +940,15 @@ int open_socket_in( int type, int port, int dlevel, uint32 socket_addr, BOOL reb /* now we've got a socket - we need to bind it */ if( bind( res, (struct sockaddr *)&sock, sizeof(sock) ) == -1 ) { - if( DEBUGLVL(dlevel) && (port == SMB_PORT1 || port == SMB_PORT2 || port == NMB_PORT) ) { + if( DEBUGLVL(dlevel) && (port == SMB_PORT1 || + port == SMB_PORT2 || port == NMB_PORT) ) { dbgtext( "bind failed on port %d ", port ); - dbgtext( "socket_addr = %s.\n", inet_ntoa( sock.sin_addr ) ); + dbgtext( "socket_addr = %s.\n", + inet_ntoa( sock.sin_addr ) ); dbgtext( "Error = %s\n", strerror(errno) ); } - close( res ); - return( -1 ); + close( res ); + return -1; } DEBUG( 10, ( "bind succeeded on port %d\n", port ) ); @@ -723,75 +957,240 @@ int open_socket_in( int type, int port, int dlevel, uint32 socket_addr, BOOL reb } /**************************************************************************** - create an outgoing socket. timeout is in milliseconds. - **************************************************************************/ + Create an outgoing socket. timeout is in milliseconds. +**************************************************************************/ int open_socket_out(int type, struct in_addr *addr, int port ,int timeout) { - struct sockaddr_in sock_out; - int res,ret; - int connect_loop = 250; /* 250 milliseconds */ - int loops = (timeout) / connect_loop; - - /* create a socket to write to */ - res = socket(PF_INET, type, 0); - if (res == -1) - { DEBUG(0,("socket error\n")); return -1; } - - if (type != SOCK_STREAM) return(res); - - memset((char *)&sock_out,'\0',sizeof(sock_out)); - putip((char *)&sock_out.sin_addr,(char *)addr); - - sock_out.sin_port = htons( port ); - sock_out.sin_family = PF_INET; - - /* set it non-blocking */ - set_blocking(res,False); - - DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port)); - - /* and connect it to the destination */ -connect_again: - ret = connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out)); - - /* Some systems return EAGAIN when they mean EINPROGRESS */ - if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY || - errno == EAGAIN) && loops--) { - msleep(connect_loop); - goto connect_again; - } - - if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY || - errno == EAGAIN)) { - DEBUG(1,("timeout connecting to %s:%d\n",inet_ntoa(*addr),port)); - close(res); - return -1; - } + struct sockaddr_in sock_out; + int res,ret; + int connect_loop = 10; + int increment = 10; + + /* create a socket to write to */ + res = socket(PF_INET, type, 0); + if (res == -1) { + DEBUG(0,("socket error (%s)\n", strerror(errno))); + return -1; + } + + if (type != SOCK_STREAM) + return(res); + + memset((char *)&sock_out,'\0',sizeof(sock_out)); + putip((char *)&sock_out.sin_addr,(char *)addr); + + sock_out.sin_port = htons( port ); + sock_out.sin_family = PF_INET; + + /* set it non-blocking */ + set_blocking(res,False); + + DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port)); + + /* and connect it to the destination */ + connect_again: + + ret = connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out)); + + /* Some systems return EAGAIN when they mean EINPROGRESS */ + if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY || + errno == EAGAIN) && (connect_loop < timeout) ) { + smb_msleep(connect_loop); + timeout -= connect_loop; + connect_loop += increment; + if (increment < 250) { + /* After 8 rounds we end up at a max of 255 msec */ + increment *= 1.5; + } + goto connect_again; + } + + if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY || + errno == EAGAIN)) { + DEBUG(1,("timeout connecting to %s:%d\n", + inet_ntoa(*addr),port)); + close(res); + return -1; + } #ifdef EISCONN - if (ret < 0 && errno == EISCONN) { - errno = 0; - ret = 0; - } + if (ret < 0 && errno == EISCONN) { + errno = 0; + ret = 0; + } #endif - if (ret < 0) { - DEBUG(2,("error connecting to %s:%d (%s)\n", - inet_ntoa(*addr),port,strerror(errno))); - close(res); - return -1; - } + if (ret < 0) { + DEBUG(2,("error connecting to %s:%d (%s)\n", + inet_ntoa(*addr),port,strerror(errno))); + close(res); + return -1; + } - /* set it blocking again */ - set_blocking(res,True); + /* set it blocking again */ + set_blocking(res,True); - return res; + return res; } -/* - open a connected UDP socket to host on port -*/ +/**************************************************************************** + Create an outgoing TCP socket to any of the addrs. This is for + simultaneous connects to port 445 and 139 of a host or even a variety + of DC's all of which are equivalent for our purposes. +**************************************************************************/ + +BOOL open_any_socket_out(struct sockaddr_in *addrs, int num_addrs, + int timeout, int *fd_index, int *fd) +{ + int i, resulting_index, res; + int *sockets; + BOOL good_connect; + + fd_set r_fds, wr_fds; + struct timeval tv; + int maxfd; + + int connect_loop = 10000; /* 10 milliseconds */ + + timeout *= 1000; /* convert to microseconds */ + + sockets = SMB_MALLOC_ARRAY(int, num_addrs); + + if (sockets == NULL) + return False; + + resulting_index = -1; + + for (i=0; imaxfd) + maxfd = sockets[i]; + } + + tv.tv_sec = 0; + tv.tv_usec = connect_loop; + + res = sys_select_intr(maxfd+1, &r_fds, &wr_fds, NULL, &tv); + + if (res < 0) + goto done; + + if (res == 0) + goto next_round; + + for (i=0; i timeout) + connect_loop = timeout; + goto connect_again; + + done: + for (i=0; i= 0) + close(sockets[i]); + } + + if (resulting_index >= 0) { + *fd_index = resulting_index; + *fd = sockets[*fd_index]; + set_blocking(*fd, True); + } + + free(sockets); + + return (resulting_index >= 0); +} +/**************************************************************************** + Open a connected UDP socket to host on port +**************************************************************************/ + int open_udp_socket(const char *host, int port) { int type = SOCK_DGRAM; @@ -819,40 +1218,21 @@ int open_udp_socket(const char *host, int port) return res; } - -/* the following 3 client_*() functions are nasty ways of allowing - some generic functions to get info that really should be hidden in - particular modules */ -static int client_fd = -1; - -void client_setfd(int fd) -{ - client_fd = fd; -} - -char *client_name(void) -{ - return get_socket_name(client_fd,False); -} - -char *client_addr(void) -{ - return get_socket_addr(client_fd); -} - /******************************************************************* - matchname - determine if host name matches IP address. Used to - confirm a hostname lookup to prevent spoof attacks - ******************************************************************/ + Matchname - determine if host name matches IP address. Used to + confirm a hostname lookup to prevent spoof attacks. +******************************************************************/ + static BOOL matchname(char *remotehost,struct in_addr addr) { struct hostent *hp; int i; - + if ((hp = sys_gethostbyname(remotehost)) == 0) { - DEBUG(0,("sys_gethostbyname(%s): lookup failure.\n", remotehost)); + DEBUG(0,("sys_gethostbyname(%s): lookup failure.\n", + remotehost)); return False; - } + } /* * Make sure that gethostbyname() returns the "correct" host name. @@ -861,38 +1241,39 @@ static BOOL matchname(char *remotehost,struct in_addr addr) * local DNS, we just have to trust it (all bets are off if the local * DNS is perverted). We always check the address list, though. */ - - if (strcasecmp(remotehost, hp->h_name) - && strcasecmp(remotehost, "localhost")) { + + if (!strequal(remotehost, hp->h_name) + && !strequal(remotehost, "localhost")) { DEBUG(0,("host name/name mismatch: %s != %s\n", remotehost, hp->h_name)); return False; } - + /* Look up the host address in the address list we just got. */ for (i = 0; hp->h_addr_list[i]; i++) { - if (memcmp(hp->h_addr_list[i], (char *) & addr, sizeof(addr)) == 0) + if (memcmp(hp->h_addr_list[i], (char *)&addr,sizeof(addr)) == 0) return True; } - + /* * The host name does not map to the original host address. Perhaps * someone has compromised a name server. More likely someone botched * it, but that could be dangerous, too. */ - + DEBUG(0,("host name/address mismatch: %s != %s\n", inet_ntoa(addr), hp->h_name)); return False; } - /******************************************************************* - return the DNS name of the remote end of a socket - ******************************************************************/ -char *get_socket_name(int fd, BOOL force_lookup) + Return the DNS name of the remote end of a socket. +******************************************************************/ + +char *get_peer_name(int fd, BOOL force_lookup) { static pstring name_buf; + pstring tmp_name; static fstring addr_buf; struct hostent *hp; struct in_addr addr; @@ -903,23 +1284,26 @@ char *get_socket_name(int fd, BOOL force_lookup) with dns. To avoid the delay we avoid the lookup if possible */ if (!lp_hostname_lookups() && (force_lookup == False)) { - return get_socket_addr(fd); + return get_peer_addr(fd); } - - p = get_socket_addr(fd); + + p = get_peer_addr(fd); /* it might be the same as the last one - save some DNS work */ - if (strcmp(p, addr_buf) == 0) return name_buf; + if (strcmp(p, addr_buf) == 0) + return name_buf; pstrcpy(name_buf,"UNKNOWN"); - if (fd == -1) return name_buf; + if (fd == -1) + return name_buf; fstrcpy(addr_buf, p); addr = *interpret_addr2(p); - + /* Look up the remote host name. */ - if ((hp = gethostbyaddr((char *)&addr.s_addr, sizeof(addr.s_addr), AF_INET)) == 0) { + if ((hp = gethostbyaddr((char *)&addr.s_addr, + sizeof(addr.s_addr), AF_INET)) == 0) { DEBUG(1,("Gethostbyaddr failed for %s\n",p)); pstrcpy(name_buf, p); } else { @@ -930,7 +1314,12 @@ char *get_socket_name(int fd, BOOL force_lookup) } } - alpha_strcpy(name_buf, name_buf, "_-.", sizeof(name_buf)); + /* can't pass the same source and dest strings in when you + use --enable-developer or the clobber_region() call will + get you */ + + pstrcpy( tmp_name, name_buf ); + alpha_strcpy(name_buf, tmp_name, "_-.", sizeof(name_buf)); if (strstr(name_buf,"..")) { pstrcpy(name_buf, "UNKNOWN"); } @@ -939,13 +1328,14 @@ char *get_socket_name(int fd, BOOL force_lookup) } /******************************************************************* - return the IP addr of the remote end of a socket as a string + Return the IP addr of the remote end of a socket as a string. ******************************************************************/ -char *get_socket_addr(int fd) + +char *get_peer_addr(int fd) { struct sockaddr sa; struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa); - int length = sizeof(sa); + socklen_t length = sizeof(sa); static fstring addr_buf; fstrcpy(addr_buf,"0.0.0.0"); @@ -953,213 +1343,110 @@ char *get_socket_addr(int fd) if (fd == -1) { return addr_buf; } - + if (getpeername(fd, &sa, &length) < 0) { - DEBUG(0,("getpeername failed. Error was %s\n", strerror(errno) )); + DEBUG(0,("getpeername failed. Error was %s\n", + strerror(errno) )); return addr_buf; } - + fstrcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr)); - + return addr_buf; } - /******************************************************************* Create protected unix domain socket. - some unixen cannot set permissions on a ux-dom-sock, so we + Some unixes cannot set permissions on a ux-dom-sock, so we have to make sure that the directory contains the protection - permissions, instead. + permissions instead. ******************************************************************/ + int create_pipe_sock(const char *socket_dir, const char *socket_name, mode_t dir_perms) { #ifdef HAVE_UNIXSOCKET - struct sockaddr_un sunaddr; - struct stat st; - int sock; - mode_t old_umask; - pstring path; - - /* Create the socket directory or reuse the existing one */ - - if (lstat(socket_dir, &st) == -1) { - - if (errno == ENOENT) { - - /* Create directory */ - - if (mkdir(socket_dir, dir_perms) == -1) { - DEBUG(0, ("error creating socket directory " - "%s: %s\n", socket_dir, - strerror(errno))); - return -1; - } - - } else { - - DEBUG(0, ("lstat failed on socket directory %s: %s\n", - socket_dir, strerror(errno))); - return -1; - } - - } else { - - /* Check ownership and permission on existing directory */ - - if (!S_ISDIR(st.st_mode)) { - DEBUG(0, ("socket directory %s isn't a directory\n", - socket_dir)); - return -1; - } - - if ((st.st_uid != sec_initial_uid()) || - ((st.st_mode & 0777) != dir_perms)) { - DEBUG(0, ("invalid permissions on socket directory " - "%s\n", socket_dir)); - return -1; - } - } - - /* Create the socket file */ - - old_umask = umask(0); - - sock = socket(AF_UNIX, SOCK_STREAM, 0); - - if (sock == -1) { - perror("socket"); - umask(old_umask); - return -1; - } - - snprintf(path, sizeof(path), "%s/%s", socket_dir, socket_name); - - unlink(path); - memset(&sunaddr, 0, sizeof(sunaddr)); - sunaddr.sun_family = AF_UNIX; - safe_strcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)-1); - - if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { - DEBUG(0, ("bind failed on pipe socket %s: %s\n", - path, - strerror(errno))); - close(sock); - umask(old_umask); - return -1; - } - - if (listen(sock, 5) == -1) { - DEBUG(0, ("listen failed on pipe socket %s: %s\n", - path, - strerror(errno))); - close(sock); - umask(old_umask); - return -1; - } - - umask(old_umask); - - /* Success! */ - - return sock; -#else - DEBUG(0, ("create_pipe_sock: No Unix sockets on this system\n")); - return -1; -#endif /* HAVE_UNIXSOCKET */ -} - -/******************************************************************* -this is like socketpair but uses tcp. It is used by the Samba -regression test code -The function guarantees that nobody else can attach to the socket, -or if they do that this function fails and the socket gets closed -returns 0 on success, -1 on failure -the resulting file descriptors are symmetrical - ******************************************************************/ -static int socketpair_tcp(int fd[2]) -{ - int listener; - struct sockaddr_in sock; - struct sockaddr_in sock2; - socklen_t socklen = sizeof(sock); - int connect_done = 0; - - fd[0] = fd[1] = listener = -1; - - memset(&sock, 0, sizeof(sock)); - - if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed; - - memset(&sock2, 0, sizeof(sock2)); -#ifdef HAVE_SOCK_SIN_LEN - sock2.sin_len = sizeof(sock2); -#endif - sock2.sin_family = PF_INET; - - bind(listener, (struct sockaddr *)&sock2, sizeof(sock2)); + struct sockaddr_un sunaddr; + struct stat st; + int sock; + mode_t old_umask; + pstring path; + + old_umask = umask(0); + + /* Create the socket directory or reuse the existing one */ + + if (lstat(socket_dir, &st) == -1) { + if (errno == ENOENT) { + /* Create directory */ + if (mkdir(socket_dir, dir_perms) == -1) { + DEBUG(0, ("error creating socket directory " + "%s: %s\n", socket_dir, + strerror(errno))); + goto out_umask; + } + } else { + DEBUG(0, ("lstat failed on socket directory %s: %s\n", + socket_dir, strerror(errno))); + goto out_umask; + } + } else { + /* Check ownership and permission on existing directory */ + if (!S_ISDIR(st.st_mode)) { + DEBUG(0, ("socket directory %s isn't a directory\n", + socket_dir)); + goto out_umask; + } + if ((st.st_uid != sec_initial_uid()) || + ((st.st_mode & 0777) != dir_perms)) { + DEBUG(0, ("invalid permissions on socket directory " + "%s\n", socket_dir)); + goto out_umask; + } + } - if (listen(listener, 1) != 0) goto failed; + /* Create the socket file */ - if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed; + sock = socket(AF_UNIX, SOCK_STREAM, 0); - if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed; + if (sock == -1) { + perror("socket"); + goto out_umask; + } - set_blocking(fd[1], 0); + pstr_sprintf(path, "%s/%s", socket_dir, socket_name); - sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + unlink(path); + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + safe_strcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)-1); - if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) == -1) { - if (errno != EINPROGRESS) goto failed; - } else { - connect_done = 1; + if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { + DEBUG(0, ("bind failed on pipe socket %s: %s\n", path, + strerror(errno))); + goto out_close; } - if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed; - - close(listener); - if (connect_done == 0) { - if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) != 0 - && errno != EISCONN) goto failed; + if (listen(sock, 5) == -1) { + DEBUG(0, ("listen failed on pipe socket %s: %s\n", path, + strerror(errno))); + goto out_close; } - set_blocking(fd[1], 1); + umask(old_umask); + return sock; - /* all OK! */ - return 0; +out_close: + close(sock); - failed: - if (fd[0] != -1) close(fd[0]); - if (fd[1] != -1) close(fd[1]); - if (listener != -1) close(listener); +out_umask: + umask(old_umask); return -1; -} - -/******************************************************************* -run a program on a local tcp socket, this is used to launch smbd -when regression testing -the return value is a socket which is attached to a subprocess -running "prog". stdin and stdout are attached. stderr is left -attached to the original stderr - ******************************************************************/ -int sock_exec(const char *prog) -{ - int fd[2]; - if (socketpair_tcp(fd) != 0) { - DEBUG(0,("socketpair_tcp failed (%s)\n", strerror(errno))); - return -1; - } - if (fork() == 0) { - close(fd[0]); - close(0); - close(1); - dup(fd[1]); - dup(fd[1]); - exit(system(prog)); - } - close(fd[1]); - return fd[0]; +#else + DEBUG(0, ("create_pipe_sock: No Unix sockets on this system\n")); + return -1; +#endif /* HAVE_UNIXSOCKET */ }