/* clear fds, close old ports */ void unbind_ports(void) { SERVICE_OPTIONS *opt; #ifdef HAVE_STRUCT_SOCKADDR_UN struct stat st; /* buffer for stat */ #endif s_poll_init(fds); s_poll_add(fds, signal_pipe[0], 1, 0); for(opt=service_options.next; opt; opt=opt->next) if(opt->option.accept && opt->fd>=0) { closesocket(opt->fd); s_log(LOG_DEBUG, "Service [%s] closed (FD=%d)", opt->servname, opt->fd); opt->fd=-1; #ifdef HAVE_STRUCT_SOCKADDR_UN if(opt->local_addr.sa.sa_family==AF_UNIX) { if(lstat(opt->local_addr.un.sun_path, &st)) sockerror(opt->local_addr.un.sun_path); else if(!S_ISSOCK(st.st_mode)) s_log(LOG_ERR, "Not a socket: %s", opt->local_addr.un.sun_path); else if(unlink(opt->local_addr.un.sun_path)) sockerror(opt->local_addr.un.sun_path); else s_log(LOG_DEBUG, "Socket removed: %s", opt->local_addr.un.sun_path); } #endif } }
void read_blocking(CLI *c, int fd, u8 *ptr, int len) { /* simulate a blocking read */ s_poll_set fds; int num; while(len>0) { s_poll_zero(&fds); s_poll_add(&fds, fd, 1, 0); /* read */ switch(s_poll_wait(&fds, c->opt->timeout_busy)) { case -1: sockerror("read_blocking: s_poll_wait"); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "read_blocking: s_poll_wait timeout"); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "read_blocking: s_poll_wait unknown result"); longjmp(c->err, 1); /* error */ } num=readsocket(fd, ptr, len); switch(num) { case -1: /* error */ sockerror("readsocket (read_blocking)"); longjmp(c->err, 1); case 0: /* EOF */ s_log(LOG_ERR, "Unexpected socket close (read_blocking)"); longjmp(c->err, 1); } ptr+=num; len-=num; } }
int drop_privileges(int critical) { #if defined(USE_WIN32) || defined(__vms) || defined(USE_OS2) (void)critical; /* squash the unused parameter warning */ #else #ifdef HAVE_SETGROUPS gid_t gr_list[1]; #endif /* set uid and gid */ if(global_options.gid) { if(setgid(global_options.gid) && critical) { sockerror("setgid"); return 1; } #ifdef HAVE_SETGROUPS gr_list[0]=global_options.gid; if(setgroups(1, gr_list) && critical) { sockerror("setgroups"); return 1; } #endif } if(global_options.uid) { if(setuid(global_options.uid) && critical) { sockerror("setuid"); return 1; } } #endif /* standard Unix */ return 0; }
/* wait for the result of a non-blocking connect */ static int connect_wait(CLI *c, int fd, int timeout) { int error; socklen_t optlen; s_log(LOG_DEBUG, "connect_wait: waiting %d seconds", timeout); s_poll_zero(&c->fds); s_poll_add(&c->fds, fd, 1, 1); switch(s_poll_wait(&c->fds, timeout)) { case -1: sockerror("connect_wait: s_poll_wait"); return -1; /* error */ case 0: s_log(LOG_INFO, "connect_wait: s_poll_wait timeout"); return -1; /* error */ default: if(s_poll_canread(&c->fds, fd)) { /* just connected socket should not be ready for read */ /* get the resulting error code, now */ optlen=sizeof(error); if(!getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &optlen)) errno=error; if(errno) { /* really an error? */ sockerror("connect_wait: getsockopt"); return -1; /* connection failed */ } } if(s_poll_canwrite(&c->fds, fd)) { s_log(LOG_DEBUG, "connect_wait: connected"); return 0; /* success */ } s_log(LOG_ERR, "connect_wait: unexpected s_poll_wait result"); return -1; /* unexpected result */ } return -1; /* should not be possible */ }
void s_write(CLI *c, int fd, const void *buf, int len) { /* simulate a blocking write */ u8 *ptr=(u8 *)buf; int num; while(len>0) { s_poll_init(c->fds); s_poll_add(c->fds, fd, 0, 1); /* write */ switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("s_write: s_poll_wait"); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "s_write: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "s_write: s_poll_wait: unknown result"); longjmp(c->err, 1); /* error */ } num=writesocket(fd, (void *)ptr, len); switch(num) { case -1: /* error */ sockerror("writesocket (s_write)"); longjmp(c->err, 1); } ptr+=num; len-=num; } }
void set_nonblock(SOCKET fd, unsigned long nonblock) { #if defined F_GETFL && defined F_SETFL && defined O_NONBLOCK && !defined __INNOTEK_LIBC__ int err, flags; do { flags=fcntl(fd, F_GETFL, 0); } while(flags<0 && get_last_socket_error()==S_EINTR); if(flags<0) { sockerror("fcntl GETFL"); /* non-critical */ return; } if(nonblock) flags|=O_NONBLOCK; else flags&=~O_NONBLOCK; do { err=fcntl(fd, F_SETFL, flags); } while(err<0 && get_last_socket_error()==S_EINTR); if(err<0) sockerror("fcntl SETFL"); /* non-critical */ #else /* WIN32 or similar */ if(ioctlsocket(fd, (long)FIONBIO, &nonblock)<0) sockerror("ioctlsocket"); /* non-critical */ #if 0 else s_log(LOG_DEBUG, "Socket %d set to %s mode", fd, nonblock ? "non-blocking" : "blocking"); #endif #endif }
void write_blocking(CLI *c, int fd, u8 *ptr, int len) { /* simulate a blocking write */ s_poll_set fds; int num; while(len>0) { s_poll_zero(&fds); s_poll_add(&fds, fd, 0, 1); /* write */ switch(s_poll_wait(&fds, c->opt->timeout_busy)) { case -1: sockerror("write_blocking: s_poll_wait"); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "write_blocking: s_poll_wait timeout"); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "write_blocking: s_poll_wait unknown result"); longjmp(c->err, 1); /* error */ } num=writesocket(fd, ptr, len); switch(num) { case -1: /* error */ sockerror("writesocket (write_blocking)"); longjmp(c->err, 1); } ptr+=num; len-=num; } }
static void local_bind(CLI * c) { if (!c->bind_addr) return; if (ntohs(c->bind_addr->in.sin_port) >= 1024) { if (!bind(c->fd, &c->bind_addr->sa, addr_len(c->bind_addr))) { s_log(LOG_INFO, "local_bind succeeded on the original port"); return; } if (get_last_socket_error() != S_EADDRINUSE) { sockerror("local_bind (original port)"); longjmp(c->err, 1); } } c->bind_addr->in.sin_port = htons(0); if (!bind(c->fd, &c->bind_addr->sa, addr_len(c->bind_addr))) { s_log(LOG_INFO, "local_bind succeeded on an ephemeral port"); return; } sockerror("local_bind (ephemeral port)"); longjmp(c->err, 1); }
int make_sockets(SOCKET fd[2]) { /* make a pair of connected ipv4 sockets */ #ifdef INET_SOCKET_PAIR struct sockaddr_in addr; socklen_t addrlen; SOCKET s; /* temporary socket awaiting for connection */ /* create two *blocking* sockets first */ s=s_socket(AF_INET, SOCK_STREAM, 0, 0, "make_sockets: s_socket#1"); if(s==INVALID_SOCKET) return 1; fd[1]=s_socket(AF_INET, SOCK_STREAM, 0, 0, "make_sockets: s_socket#2"); if(fd[1]==INVALID_SOCKET) { closesocket(s); return 1; } addrlen=sizeof addr; memset(&addr, 0, sizeof addr); addr.sin_family=AF_INET; addr.sin_addr.s_addr=htonl(INADDR_LOOPBACK); addr.sin_port=htons(0); /* dynamic port allocation */ if(bind(s, (struct sockaddr *)&addr, addrlen)) log_error(LOG_DEBUG, get_last_socket_error(), "make_sockets: bind#1"); if(bind(fd[1], (struct sockaddr *)&addr, addrlen)) log_error(LOG_DEBUG, get_last_socket_error(), "make_sockets: bind#2"); if(listen(s, 1)) { sockerror("make_sockets: listen"); closesocket(s); closesocket(fd[1]); return 1; } if(getsockname(s, (struct sockaddr *)&addr, &addrlen)) { sockerror("make_sockets: getsockname"); closesocket(s); closesocket(fd[1]); return 1; } if(connect(fd[1], (struct sockaddr *)&addr, addrlen)) { sockerror("make_sockets: connect"); closesocket(s); closesocket(fd[1]); return 1; } fd[0]=s_accept(s, (struct sockaddr *)&addr, &addrlen, 1, "make_sockets: s_accept"); if(fd[0]==INVALID_SOCKET) { closesocket(s); closesocket(fd[1]); return 1; } closesocket(s); /* don't care about the result */ set_nonblock(fd[0], 1); set_nonblock(fd[1], 1); #else if(s_socketpair(AF_UNIX, SOCK_STREAM, 0, fd, 1, "make_sockets: socketpair")) return 1; #endif return 0; }
static void init_local(CLI * c) { SOCKADDR_UNION addr; socklen_t addr_len; char *accepted_address; addr_len = sizeof(SOCKADDR_UNION); c->local_rfd.is_socket = !getpeername(c->local_rfd.fd, &addr.sa, &addr_len); if (c->local_rfd.is_socket) { memcpy(&c->peer_addr.sa, &addr.sa, addr_len); c->peer_addr_len = addr_len; if (set_socket_options(c->local_rfd.fd, 1)) s_log(LOG_WARNING, "Failed to set local socket options"); } else { if (get_last_socket_error() != S_ENOTSOCK) { sockerror("getpeerbyname (local_rfd)"); longjmp(c->err, 1); } } if (c->local_rfd.fd == c->local_wfd.fd) { c->local_wfd.is_socket = c->local_rfd.is_socket; } else { addr_len = sizeof(SOCKADDR_UNION); c->local_wfd.is_socket = !getpeername(c->local_wfd.fd, &addr.sa, &addr_len); if (c->local_wfd.is_socket) { if (!c->local_rfd.is_socket) { memcpy(&c->peer_addr.sa, &addr.sa, addr_len); c->peer_addr_len = addr_len; } if (set_socket_options(c->local_wfd.fd, 1)) s_log(LOG_WARNING, "Failed to set local socket options"); } else { if (get_last_socket_error() != S_ENOTSOCK) { sockerror("getpeerbyname (local_wfd)"); longjmp(c->err, 1); } } } if (!c->local_rfd.is_socket && !c->local_rfd.is_socket) { s_log(LOG_NOTICE, "Service [%s] accepted connection", c->opt->servname); return; } accepted_address = s_ntop(&c->peer_addr, c->peer_addr_len); auth_user(c, accepted_address); s_log(LOG_NOTICE, "Service [%s] accepted connection from %s", c->opt->servname, accepted_address); str_free(accepted_address); }
void s_read(CLI *c, int fd, void *ptr, int len) { /* simulate a blocking read */ int num; while(len>0) { s_poll_init(c->fds); s_poll_add(c->fds, fd, 1, 0); /* read */ switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("s_read: s_poll_wait"); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "s_read: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "s_read: s_poll_wait: unknown result"); longjmp(c->err, 1); /* error */ } num=readsocket(fd, ptr, len); switch(num) { case -1: /* error */ sockerror("readsocket (s_read)"); longjmp(c->err, 1); case 0: /* EOF */ s_log(LOG_ERR, "Unexpected socket close (s_read)"); longjmp(c->err, 1); } ptr=(u8 *)ptr+num; len-=num; } }
int drop_privileges(int critical) { #ifdef HAVE_SETGROUPS gid_t gr_list[1]; #endif /* set uid and gid */ if(global_options.gid) { if(setgid(global_options.gid) && critical) { sockerror("setgid"); return 1; } #ifdef HAVE_SETGROUPS gr_list[0]=global_options.gid; if(setgroups(1, gr_list) && critical) { sockerror("setgroups"); return 1; } #endif } if(global_options.uid) { if(setuid(global_options.uid) && critical) { sockerror("setuid"); return 1; } } return 0; }
static int connect_remote(CLI *c) { /* connect to remote host */ SOCKADDR_UNION bind_addr, addr; SOCKADDR_LIST resolved_list, *address_list; int error; int s; /* destination socket */ u16 i; /* setup address_list */ if(c->opt->option.delayed_lookup) { resolved_list.num=0; if(!name2addrlist(&resolved_list, c->opt->remote_address, DEFAULT_LOOPBACK)) return -1; /* no host resolved */ address_list=&resolved_list; } else /* use pre-resolved addresses */ address_list=&c->opt->remote_addr; /* try to connect each host from the list */ for(i=0; i<address_list->num; i++) { memcpy(&addr, address_list->addr + address_list->cur, sizeof(SOCKADDR_UNION)); address_list->cur=(address_list->cur+1)%address_list->num; /* race condition is possible, but harmless in this case */ if((s=socket(addr.sa.sa_family, SOCK_STREAM, 0))<0) { sockerror("remote socket"); return -1; } if(alloc_fd(s)) return -1; if(c->bind_addr.num) { /* explicit local bind or transparent proxy */ memcpy(&bind_addr, &c->bind_addr.addr[0], sizeof(SOCKADDR_UNION)); if(bind(s, &bind_addr.sa, addr_len(bind_addr))<0) { sockerror("bind transparent"); closesocket(s); return -1; } } /* try to connect for the 1st time */ s_ntop(c->connecting_address, &addr); s_log(LOG_DEBUG, "%s connecting %s", c->opt->servname, c->connecting_address); if(!connect(s, &addr.sa, addr_len(addr))) return s; /* no error -> success (should not be possible) */ error=get_last_socket_error(); if(error!=EINPROGRESS && error!=EWOULDBLOCK) { s_log(LOG_ERR, "remote connect (%s): %s (%d)", c->connecting_address, my_strerror(error), error); closesocket(s); continue; /* next IP */ } if(!connect_wait(c, s, c->opt->timeout_connect)) return s; /* success! */ closesocket(s); /* error -> next IP */ } return -1; }
/* open new ports, update fds */ int bind_ports(void) { SERVICE_OPTIONS *opt; char *local_address; #ifdef USE_LIBWRAP /* execute after parse_commandline() to know service_options.next, * but as early as possible to avoid leaking file descriptors */ /* retry on each bind_ports() in case stunnel.conf was reloaded without "libwrap = no" */ libwrap_init(); #endif /* USE_LIBWRAP */ s_poll_init(fds); s_poll_add(fds, signal_pipe[0], 1, 0); /* allow clean unbind_ports() even though bind_ports() was not fully performed */ for(opt=service_options.next; opt; opt=opt->next) if(opt->option.accept) opt->fd=-1; for(opt=service_options.next; opt; opt=opt->next) { if(opt->option.accept) { opt->fd=s_socket(opt->local_addr.sa.sa_family, SOCK_STREAM, 0, 1, "accept socket"); if(opt->fd<0) return 1; if(set_socket_options(opt->fd, 0)<0) { closesocket(opt->fd); return 1; } /* local socket can't be unnamed */ local_address=s_ntop(&opt->local_addr, addr_len(&opt->local_addr)); if(bind(opt->fd, &opt->local_addr.sa, addr_len(&opt->local_addr))) { s_log(LOG_ERR, "Error binding service [%s] to %s", opt->servname, local_address); sockerror("bind"); closesocket(opt->fd); str_free(local_address); return 1; } if(listen(opt->fd, SOMAXCONN)) { sockerror("listen"); closesocket(opt->fd); str_free(local_address); return 1; } s_poll_add(fds, opt->fd, 1, 0); s_log(LOG_DEBUG, "Service [%s] (FD=%d) bound to %s", opt->servname, opt->fd, local_address); str_free(local_address); } else if(opt->option.program && opt->option.remote) { /* create exec+connect services */ create_client(-1, -1, alloc_client_session(opt, -1, -1), client_thread); } } return 0; /* OK */ }
char *fd_getline(CLI *c, int fd) { char *line, *tmpline; int ptr=0, allocated=32; line=str_alloc(allocated); for(;;) { s_poll_init(c->fds); s_poll_add(c->fds, fd, 1, 0); /* read */ switch(s_poll_wait(c->fds, c->opt->timeout_busy, 0)) { case -1: sockerror("fd_getline: s_poll_wait"); str_free(line); longjmp(c->err, 1); /* error */ case 0: s_log(LOG_INFO, "fd_getline: s_poll_wait:" " TIMEOUTbusy exceeded: sending reset"); str_free(line); longjmp(c->err, 1); /* timeout */ case 1: break; /* OK */ default: s_log(LOG_ERR, "fd_getline: s_poll_wait: Unknown result"); str_free(line); longjmp(c->err, 1); /* error */ } if(allocated<ptr+1) { allocated*=2; line=str_realloc(line, allocated); } switch(readsocket(fd, line+ptr, 1)) { case -1: /* error */ sockerror("fd_getline: readsocket"); str_free(line); longjmp(c->err, 1); case 0: /* EOF */ s_log(LOG_ERR, "fd_getline: Unexpected socket close"); str_free(line); longjmp(c->err, 1); } if(line[ptr]=='\r') continue; if(line[ptr]=='\n') break; if(line[ptr]=='\0') break; if(++ptr>65536) { /* >64KB --> DoS protection */ s_log(LOG_ERR, "fd_getline: Line too long"); str_free(line); longjmp(c->err, 1); } } line[ptr]='\0'; tmpline=str_dup(line); safestring(tmpline); s_log(LOG_DEBUG, " <- %s", tmpline); str_free(tmpline); return line; }
static int auth_user(CLI *c) { struct servent *s_ent; /* structure for getservbyname */ SOCKADDR_UNION ident; /* IDENT socket name */ int fd; /* IDENT socket descriptor */ char name[STRLEN]; int retval; int error; if(!c->opt->username) return 0; /* -u option not specified */ if((fd=socket(c->peer_addr.addr[0].sa.sa_family, SOCK_STREAM, 0))<0) { sockerror("socket (auth_user)"); return -1; } if(alloc_fd(fd)) return -1; memcpy(&ident, &c->peer_addr.addr[0], sizeof(SOCKADDR_UNION)); s_ent=getservbyname("auth", "tcp"); if(s_ent) { ident.in.sin_port=s_ent->s_port; } else { s_log(LOG_WARNING, "Unknown service 'auth': using default 113"); ident.in.sin_port=htons(113); } if(connect(fd, &ident.sa, addr_len(ident))) { error=get_last_socket_error(); if(error!=EINPROGRESS && error!=EWOULDBLOCK) { sockerror("ident connect (auth_user)"); closesocket(fd); return -1; } if(connect_wait(c, fd, c->opt->timeout_connect)) { /* error */ closesocket(fd); return -1; } } s_log(LOG_DEBUG, "IDENT server connected"); if(fdprintf(c, fd, "%u , %u", ntohs(c->peer_addr.addr[0].in.sin_port), ntohs(c->opt->local_addr.addr[0].in.sin_port))<0) { sockerror("fdprintf (auth_user)"); closesocket(fd); return -1; } if(fdscanf(c, fd, "%*[^:]: USERID :%*[^:]:%s", name)!=1) { s_log(LOG_ERR, "Incorrect data from IDENT server"); closesocket(fd); return -1; } closesocket(fd); retval=strcmp(name, c->opt->username) ? -1 : 0; safestring(name); s_log(LOG_INFO, "IDENT resolved remote user to %s", name); return retval; }
NOEXPORT int change_root(void) { if(!global_options.chroot_dir) return 0; if(chroot(global_options.chroot_dir)) { sockerror("chroot"); return 1; } if(chdir("/")) { sockerror("chdir"); return 1; } return 0; }
static void make_sockets(CLI *c, int fd[2]) { /* make a pair of connected sockets */ #ifdef INET_SOCKET_PAIR SOCKADDR_UNION addr; socklen_t addrlen; int s; /* temporary socket awaiting for connection */ s=s_socket(AF_INET, SOCK_STREAM, 0, 1, "socket#1"); if(s<0) longjmp(c->err, 1); c->fd=s_socket(AF_INET, SOCK_STREAM, 0, 1, "socket#2"); if(c->fd<0) longjmp(c->err, 1); addrlen=sizeof addr; memset(&addr, 0, addrlen); addr.in.sin_family=AF_INET; addr.in.sin_addr.s_addr=htonl(INADDR_LOOPBACK); addr.in.sin_port=htons(0); /* dynamic port allocation */ if(bind(s, &addr.sa, addrlen)) log_error(LOG_DEBUG, get_last_socket_error(), "bind#1"); if(bind(c->fd, &addr.sa, addrlen)) log_error(LOG_DEBUG, get_last_socket_error(), "bind#2"); if(listen(s, 1)) { closesocket(s); sockerror("listen"); longjmp(c->err, 1); } if(getsockname(s, &addr.sa, &addrlen)) { closesocket(s); sockerror("getsockname"); longjmp(c->err, 1); } if(connect_blocking(c, &addr, addr_len(addr))) { closesocket(s); longjmp(c->err, 1); } fd[0]=s_accept(s, &addr.sa, &addrlen, 1, "accept"); if(fd[0]<0) { closesocket(s); longjmp(c->err, 1); } fd[1]=c->fd; c->fd=-1; closesocket(s); /* don't care about the result */ #else if(s_socketpair(AF_UNIX, SOCK_STREAM, 0, fd, 1, "socketpair")) longjmp(c->err, 1); #endif }
int set_dmesg_loglevel(int fd, int level) { if (klogctl(8, NULL, level) == -1) return sockerror(fd, "klogctl(8)"); return sockprint(fd, "dmesg loglevel set to %d\r\n", level); }
static int parse_socket_error(CLI * c, const char *text) { switch (get_last_socket_error()) { case 0: case EPIPE: case S_ECONNABORTED: s_log(LOG_INFO, "%s: Socket is closed", text); return 0; case S_EINTR: s_log(LOG_DEBUG, "%s: Interrupted by a signal: retrying", text); return 1; case S_EWOULDBLOCK: s_log(LOG_NOTICE, "%s: Would block: retrying", text); sleep(1); return 1; #if S_EAGAIN!=S_EWOULDBLOCK case S_EAGAIN: s_log(LOG_DEBUG, "%s: Temporary lack of resources: retrying", text); return 1; #endif default: sockerror(text); longjmp(c->err, 1); } }
/** * Sends an HB_RESP in response to an HB_REQ */ void send_hb_response(const struct sockaddr_in *src, int response) { unsigned char *packet; struct uftp_h *header; struct hb_resp_h *hbresp; int meslen; packet = calloc(sizeof(struct uftp_h) + sizeof(struct hb_resp_h), 1); if (packet == NULL) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)packet; hbresp = (struct hb_resp_h *)(packet + sizeof(struct uftp_h)); header->uftp_id = UFTP_VER_NUM; header->func = HB_RESP; header->blsize = ntohs(sizeof(struct hb_resp_h)); hbresp->func = HB_RESP; hbresp->authenticated = response; if (response == HB_AUTH_CHALLENGE) { hbresp->nonce = htonl(down_nonce); } meslen = sizeof(struct uftp_h) + sizeof(struct hb_resp_h); if (nb_sendto(listener, packet, meslen, 0, (struct sockaddr *)src, sizeof(struct sockaddr_in)) == SOCKET_ERROR) { sockerror(0, 0, "Error sending HB_RESP"); } else { log(0, 0, "Sent HB_RESP to %s:%d", inet_ntoa(src->sin_addr), ntohs(src->sin_port)); } free(packet); }
/* returns 0 on close and 1 on non-critical errors */ static int parse_socket_error(CLI *c, const char *text) { switch(get_last_socket_error()) { /* http://tangentsoft.net/wskfaq/articles/bsd-compatibility.html */ case 0: /* close on read, or close on write on WIN32 */ #ifndef USE_WIN32 case EPIPE: /* close on write on Unix */ #endif case S_ECONNABORTED: s_log(LOG_INFO, "%s: Socket is closed", text); return 0; case S_EINTR: s_log(LOG_DEBUG, "%s: Interrupted by a signal: retrying", text); return 1; case S_EWOULDBLOCK: s_log(LOG_NOTICE, "%s: Would block: retrying", text); sleep(1); /* Microsoft bug KB177346 */ return 1; #if S_EAGAIN!=S_EWOULDBLOCK case S_EAGAIN: s_log(LOG_DEBUG, "%s: Temporary lack of resources: retrying", text); return 1; #endif default: sockerror(text); longjmp(c->err, 1); } }
static SOCKADDR_LIST *dynamic_remote_addr(CLI *c) { #ifdef SO_ORIGINAL_DST socklen_t addrlen=sizeof(SOCKADDR_UNION); #endif /* SO_ORIGINAL_DST */ /* check if the address was already set by a dynamic protocol * implemented protocols: CONNECT * protocols to be implemented: SOCKS4 */ if(c->connect_addr.num) return &c->connect_addr; #ifdef SO_ORIGINAL_DST if(c->opt->option.transparent_dst) { c->connect_addr.num=1; c->connect_addr.addr=str_alloc(sizeof(SOCKADDR_UNION)); if(getsockopt(c->local_rfd.fd, SOL_IP, SO_ORIGINAL_DST, c->connect_addr.addr, &addrlen)) { sockerror("setsockopt SO_ORIGINAL_DST"); longjmp(c->err, 1); } return &c->connect_addr; } #endif /* SO_ORIGINAL_DST */ if(c->opt->option.delayed_lookup) { if(!name2addrlist(&c->connect_addr, c->opt->connect_name, DEFAULT_LOOPBACK)) { s_log(LOG_ERR, "No host resolved"); longjmp(c->err, 1); } return &c->connect_addr; } return &c->opt->connect_addr; /* use pre-resolved (static) addresses */ }
static int print_socket_options(void) { int fd, len; SOCK_OPT *ptr; OPT_UNION val; char line[STRLEN]; fd=socket(AF_INET, SOCK_STREAM, 0); log_raw("Socket option defaults:"); log_raw(" %-16s%-10s%-10s%-10s%-10s", "Option", "Accept", "Local", "Remote", "OS default"); for(ptr=sock_opts; ptr->opt_str; ptr++) { /* display option name */ sprintf(line, " %-16s", ptr->opt_str); /* display stunnel default values */ print_option(line, ptr->opt_type, ptr->opt_val[0]); print_option(line, ptr->opt_type, ptr->opt_val[1]); print_option(line, ptr->opt_type, ptr->opt_val[2]); /* display OS default value */ len = sizeof(val); if(getsockopt(fd, ptr->opt_level, ptr->opt_name, (void *)&val, &len)) { if(get_last_socket_error()!=ENOPROTOOPT) { log_raw("%s", line); /* dump the name and assigned values */ sockerror("getsockopt"); return 0; /* FAILED */ } safeconcat(line, " -- "); /* write-only value */ } else print_option(line, ptr->opt_type, &val); log_raw("%s", line); } return 1; /* OK */ }
/** * Sends out a data packet. All headers should be populated except blsize */ int send_data(const struct finfo_t *finfo, unsigned char *packet, int datalen, unsigned char *encpacket) { struct uftp_h *header; struct fileseg_h *fileseg; int payloadlen; unsigned char *outpacket; header = (struct uftp_h *)packet; fileseg = (struct fileseg_h *)(packet + sizeof(struct uftp_h)); payloadlen = sizeof(struct fileseg_h) + datalen; if (keytype != KEY_NONE) { if (!encrypt_and_sign(packet, &encpacket, payloadlen, mtu, keytype, groupkey, groupsalt, ivlen, hashtype, grouphmackey, hmaclen, sigtype, privkey, rsalen)) { log0(0, 0, "Error encrypting FILESEG"); return 0; } outpacket = encpacket; payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize); } else { outpacket = packet; header->blsize = htons(payloadlen); } if (nb_sendto(sock, outpacket, payloadlen + sizeof(struct uftp_h), 0, (struct sockaddr *)&receive_dest, sizeof(receive_dest)) == SOCKET_ERROR) { sockerror(0, 0, "Error sending FILESEG"); return 0; } return 1; }
int set_socket_options(int s, int type) { SOCK_OPT *ptr; extern SOCK_OPT sock_opts[]; static char *type_str[3]={"accept", "local", "remote"}; int opt_size; for(ptr=sock_opts;ptr->opt_str;ptr++) { if(!ptr->opt_val[type]) continue; /* default */ switch(ptr->opt_type) { case TYPE_LINGER: opt_size=sizeof(struct linger); break; case TYPE_TIMEVAL: opt_size=sizeof(struct timeval); break; case TYPE_STRING: opt_size=strlen(ptr->opt_val[type]->c_val)+1; break; default: opt_size=sizeof(int); break; } if(setsockopt(s, ptr->opt_level, ptr->opt_name, (void *)ptr->opt_val[type], opt_size)) { sockerror(ptr->opt_str); return -1; /* FAILED */ } else { s_log(LOG_DEBUG, "%s option set on %s socket", ptr->opt_str, type_str[type]); } } return 0; /* OK */ }
static void udpread(void) { char buf[BUFSIZE]; int ret = recv(sockfd, buf, BUFSIZE, 0); if (ret < 0) { sockerror("recv (udp)"); x_closesocket(sockfd); exit(1); } else if (ret > 0) { #ifdef _WIN32 int j; for (j = 0; j < ret; j++) putchar(buf[j]); #else if (write(1, buf, ret) < ret) { perror("write"); exit(1); } #endif } }
/* clear fds, close old ports */ void unbind_ports(void) { SERVICE_OPTIONS *opt; #ifdef HAVE_STRUCT_SOCKADDR_UN struct stat sb; /* buffer for stat */ #endif s_poll_init(fds); s_poll_add(fds, signal_pipe[0], 1, 0); for(opt=service_options.next; opt; opt=opt->next) { s_log(LOG_DEBUG, "Closing service [%s]", opt->servname); if(opt->option.accept && opt->fd!=INVALID_SOCKET) { if(opt->fd<(SOCKET)listen_fds_start || opt->fd>=(SOCKET)(listen_fds_start+systemd_fds)) closesocket(opt->fd); s_log(LOG_DEBUG, "Service [%s] closed (FD=%d)", opt->servname, opt->fd); opt->fd=INVALID_SOCKET; #ifdef HAVE_STRUCT_SOCKADDR_UN if(opt->local_addr.sa.sa_family==AF_UNIX) { if(lstat(opt->local_addr.un.sun_path, &sb)) sockerror(opt->local_addr.un.sun_path); else if(!S_ISSOCK(sb.st_mode)) s_log(LOG_ERR, "Not a socket: %s", opt->local_addr.un.sun_path); else if(unlink(opt->local_addr.un.sun_path)) sockerror(opt->local_addr.un.sun_path); else s_log(LOG_DEBUG, "Socket removed: %s", opt->local_addr.un.sun_path); } #endif } else if(opt->exec_name && opt->connect_addr.names) { /* create exec+connect services */ /* FIXME: this is just a crude workaround */ /* is it better to kill the service? */ opt->option.retry=0; } /* purge session cache of the old SSL_CTX object */ /* this workaround won't be needed anymore after */ /* delayed deallocation calls SSL_CTX_free() */ if(opt->ctx) SSL_CTX_flush_sessions(opt->ctx, (long)time(NULL)+opt->session_timeout+1); s_log(LOG_DEBUG, "Service [%s] closed", opt->servname); } }
int dump_virtual_console(int fd_out, int fd_in) { struct{ unsigned char lines, cols, x, y; } scrn; int x, y; if (lseek(fd_in, 0, SEEK_SET) == -1) return sockerror(fd_out, "lseek"); if (read(fd_in, &scrn, 4) == -1) return sockerror(fd_out, "read on vcs"); for(y=0; y<scrn.lines; y++) { int nspaces = 0; for(x=0; x<scrn.cols; x++) { int loop; char ca[2]; if (read(fd_in, ca, 2) == -1) return sockerror(fd_out, "read on vcs (data)"); if (ca[0] != ' ') { for(loop=0; loop<nspaces; loop++) { if (sockprint(fd_out, " ") == -1) return -1; } nspaces = 0; if (sockprint(fd_out, "%c", ca[0]) == -1) return -1; } else { nspaces++; } } if (sockprint(fd_out, "\r\n") == -1) return -1; } return 0; }
NOEXPORT void proxy_server(CLI *c) { SOCKADDR_UNION addr; socklen_t addrlen; char src_host[IP_LEN], dst_host[IP_LEN]; char src_port[PORT_LEN], dst_port[PORT_LEN], *proto; int err; addrlen=sizeof addr; if(getpeername(c->local_rfd.fd, &addr.sa, &addrlen)) { sockerror("getpeername"); longjmp(c->err, 1); } err=getnameinfo(&addr.sa, addr_len(&addr), src_host, IP_LEN, src_port, PORT_LEN, NI_NUMERICHOST|NI_NUMERICSERV); if(err) { s_log(LOG_ERR, "getnameinfo: %s", s_gai_strerror(err)); longjmp(c->err, 1); } addrlen=sizeof addr; if(getsockname(c->local_rfd.fd, &addr.sa, &addrlen)) { sockerror("getsockname"); longjmp(c->err, 1); } err=getnameinfo(&addr.sa, addr_len(&addr), dst_host, IP_LEN, dst_port, PORT_LEN, NI_NUMERICHOST|NI_NUMERICSERV); if(err) { s_log(LOG_ERR, "getnameinfo: %s", s_gai_strerror(err)); longjmp(c->err, 1); } switch(addr.sa.sa_family) { case AF_INET: proto="TCP4"; break; #ifdef USE_IPv6 case AF_INET6: proto="TCP6"; break; #endif default: /* AF_UNIX */ proto="UNKNOWN"; } fd_printf(c, c->remote_fd.fd, "PROXY %s %s %s %s %s", proto, src_host, dst_host, src_port, dst_port); }