📄 socket_wrapper.c
字号:
/* * Copyright (C) Jelmer Vernooij 2005,2008 <jelmer@samba.org> * Copyright (C) Stefan Metzmacher 2006 <metze@samba.org> * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *//* Socket wrapper library. Passes all socket communication over unix domain sockets if the environment variable SOCKET_WRAPPER_DIR is set.*/#ifdef _SAMBA_BUILD_#define SOCKET_WRAPPER_NOT_REPLACE#include "lib/replace/replace.h"#include "system/network.h"#include "system/filesys.h"#include "system/time.h"#else /* _SAMBA_BUILD_ */#include <sys/types.h>#include <sys/time.h>#include <sys/stat.h>#include <sys/socket.h>#include <sys/ioctl.h>#include <sys/filio.h>#include <errno.h>#include <sys/un.h>#include <netinet/in.h>#include <netinet/tcp.h>#include <fcntl.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <stdio.h>#include <stdint.h>#endif#ifndef _PUBLIC_#define _PUBLIC_#endif#define SWRAP_DLIST_ADD(list,item) do { \ if (!(list)) { \ (item)->prev = NULL; \ (item)->next = NULL; \ (list) = (item); \ } else { \ (item)->prev = NULL; \ (item)->next = (list); \ (list)->prev = (item); \ (list) = (item); \ } \} while (0)#define SWRAP_DLIST_REMOVE(list,item) do { \ if ((list) == (item)) { \ (list) = (item)->next; \ if (list) { \ (list)->prev = NULL; \ } \ } else { \ if ((item)->prev) { \ (item)->prev->next = (item)->next; \ } \ if ((item)->next) { \ (item)->next->prev = (item)->prev; \ } \ } \ (item)->prev = NULL; \ (item)->next = NULL; \} while (0)/* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support * for now */#define REWRITE_CALLS #ifdef REWRITE_CALLS#define real_accept accept#define real_connect connect#define real_bind bind#define real_listen listen#define real_getpeername getpeername#define real_getsockname getsockname#define real_getsockopt getsockopt#define real_setsockopt setsockopt#define real_recvfrom recvfrom#define real_sendto sendto#define real_ioctl ioctl#define real_recv recv#define real_send send#define real_socket socket#define real_close close#endif#ifdef HAVE_GETTIMEOFDAY_TZ#define swrapGetTimeOfDay(tval) gettimeofday(tval,NULL)#else#define swrapGetTimeOfDay(tval) gettimeofday(tval)#endif/* we need to use a very terse format here as IRIX 6.4 silently truncates names to 16 chars, so if we use a longer name then we can't tell which port a packet came from with recvfrom() with this format we have 8 chars left for the directory name*/#define SOCKET_FORMAT "%c%02X%04X"#define SOCKET_TYPE_CHAR_TCP 'T'#define SOCKET_TYPE_CHAR_UDP 'U'#define SOCKET_TYPE_CHAR_TCP_V6 'X'#define SOCKET_TYPE_CHAR_UDP_V6 'Y'#define MAX_WRAPPED_INTERFACES 16#define SW_IPV6_ADDRESS 1static struct sockaddr *sockaddr_dup(const void *data, socklen_t len){ struct sockaddr *ret = (struct sockaddr *)malloc(len); memcpy(ret, data, len); return ret;}static void set_port(int family, int prt, struct sockaddr *addr){ switch (family) { case AF_INET: ((struct sockaddr_in *)addr)->sin_port = htons(prt); break;#ifdef HAVE_IPV6 case AF_INET6: ((struct sockaddr_in6 *)addr)->sin6_port = htons(prt); break;#endif }}static size_t socket_length(int family){ switch (family) { case AF_INET: return sizeof(struct sockaddr_in);#ifdef HAVE_IPV6 case AF_INET6: return sizeof(struct sockaddr_in6);#endif } return 0;}struct socket_info{ int fd; int family; int type; int protocol; int bound; int bcast; int is_server; char *path; char *tmp_path; struct sockaddr *myname; socklen_t myname_len; struct sockaddr *peername; socklen_t peername_len; struct { unsigned long pck_snd; unsigned long pck_rcv; } io; struct socket_info *prev, *next;};static struct socket_info *sockets;const char *socket_wrapper_dir(void){ const char *s = getenv("SOCKET_WRAPPER_DIR"); if (s == NULL) { return NULL; } if (strncmp(s, "./", 2) == 0) { s += 2; } return s;}unsigned int socket_wrapper_default_iface(void){ const char *s = getenv("SOCKET_WRAPPER_DEFAULT_IFACE"); if (s) { unsigned int iface; if (sscanf(s, "%u", &iface) == 1) { if (iface >= 1 && iface <= MAX_WRAPPED_INTERFACES) { return iface; } } } return 1;/* 127.0.0.1 */}static int convert_un_in(const struct sockaddr_un *un, struct sockaddr *in, socklen_t *len){ unsigned int iface; unsigned int prt; const char *p; char type; p = strrchr(un->sun_path, '/'); if (p) p++; else p = un->sun_path; if (sscanf(p, SOCKET_FORMAT, &type, &iface, &prt) != 3) { errno = EINVAL; return -1; } if (iface == 0 || iface > MAX_WRAPPED_INTERFACES) { errno = EINVAL; return -1; } if (prt > 0xFFFF) { errno = EINVAL; return -1; } switch(type) { case SOCKET_TYPE_CHAR_TCP: case SOCKET_TYPE_CHAR_UDP: { struct sockaddr_in *in2 = (struct sockaddr_in *)in; if ((*len) < sizeof(*in2)) { errno = EINVAL; return -1; } memset(in2, 0, sizeof(*in2)); in2->sin_family = AF_INET; in2->sin_addr.s_addr = htonl((127<<24) | iface); in2->sin_port = htons(prt); *len = sizeof(*in2); break; }#ifdef HAVE_IPV6 case SOCKET_TYPE_CHAR_TCP_V6: case SOCKET_TYPE_CHAR_UDP_V6: { struct sockaddr_in6 *in2 = (struct sockaddr_in6 *)in; if ((*len) < sizeof(*in2)) { errno = EINVAL; return -1; } memset(in2, 0, sizeof(*in2)); in2->sin6_family = AF_INET6; in2->sin6_addr.s6_addr[0] = SW_IPV6_ADDRESS; in2->sin6_port = htons(prt); *len = sizeof(*in2); break; }#endif default: errno = EINVAL; return -1; } return 0;}static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *inaddr, struct sockaddr_un *un, int *bcast){ char type = '\0'; unsigned int prt; unsigned int iface; int is_bcast = 0; if (bcast) *bcast = 0; switch (si->family) { case AF_INET: { const struct sockaddr_in *in = (const struct sockaddr_in *)inaddr; unsigned int addr = ntohl(in->sin_addr.s_addr); char u_type = '\0'; char b_type = '\0'; char a_type = '\0'; switch (si->type) { case SOCK_STREAM: u_type = SOCKET_TYPE_CHAR_TCP; break; case SOCK_DGRAM: u_type = SOCKET_TYPE_CHAR_UDP; a_type = SOCKET_TYPE_CHAR_UDP; b_type = SOCKET_TYPE_CHAR_UDP; break; } prt = ntohs(in->sin_port); if (a_type && addr == 0xFFFFFFFF) { /* 255.255.255.255 only udp */ is_bcast = 2; type = a_type; iface = socket_wrapper_default_iface(); } else if (b_type && addr == 0x7FFFFFFF) { /* 127.255.255.255 only udp */ is_bcast = 1; type = b_type; iface = socket_wrapper_default_iface(); } else if ((addr & 0xFFFFFF00) == 0x7F000000) { /* 127.0.0.X */ is_bcast = 0; type = u_type; iface = (addr & 0x000000FF); } else { errno = ENETUNREACH; return -1; } if (bcast) *bcast = is_bcast; break; }#ifdef HAVE_IPV6 case AF_INET6: { const struct sockaddr_in6 *in = (const struct sockaddr_in6 *)inaddr; switch (si->type) { case SOCK_STREAM: type = SOCKET_TYPE_CHAR_TCP_V6; break; case SOCK_DGRAM: type = SOCKET_TYPE_CHAR_UDP_V6; break; } /* XXX no multicast/broadcast */ prt = ntohs(in->sin6_port); iface = SW_IPV6_ADDRESS; break; }#endif default: errno = ENETUNREACH; return -1; } if (prt == 0) { errno = EINVAL; return -1; } if (is_bcast) { snprintf(un->sun_path, sizeof(un->sun_path), "%s/EINVAL", socket_wrapper_dir()); /* the caller need to do more processing */ return 0; } snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, socket_wrapper_dir(), type, iface, prt); return 0;}static int convert_in_un_alloc(struct socket_info *si, const struct sockaddr *inaddr, struct sockaddr_un *un, int *bcast){ char type = '\0'; unsigned int prt; unsigned int iface; struct stat st; int is_bcast = 0; if (bcast) *bcast = 0; switch (si->family) { case AF_INET: { const struct sockaddr_in *in = (const struct sockaddr_in *)inaddr; unsigned int addr = ntohl(in->sin_addr.s_addr); char u_type = '\0'; char d_type = '\0'; char b_type = '\0'; char a_type = '\0'; prt = ntohs(in->sin_port); switch (si->type) { case SOCK_STREAM: u_type = SOCKET_TYPE_CHAR_TCP; d_type = SOCKET_TYPE_CHAR_TCP; break; case SOCK_DGRAM: u_type = SOCKET_TYPE_CHAR_UDP; d_type = SOCKET_TYPE_CHAR_UDP; a_type = SOCKET_TYPE_CHAR_UDP; b_type = SOCKET_TYPE_CHAR_UDP; break; } if (addr == 0) { /* 0.0.0.0 */ is_bcast = 0; type = d_type; iface = socket_wrapper_default_iface(); } else if (a_type && addr == 0xFFFFFFFF) { /* 255.255.255.255 only udp */ is_bcast = 2; type = a_type; iface = socket_wrapper_default_iface(); } else if (b_type && addr == 0x7FFFFFFF) { /* 127.255.255.255 only udp */ is_bcast = 1; type = b_type; iface = socket_wrapper_default_iface(); } else if ((addr & 0xFFFFFF00) == 0x7F000000) { /* 127.0.0.X */ is_bcast = 0; type = u_type; iface = (addr & 0x000000FF); } else { errno = EADDRNOTAVAIL; return -1; } break; }#ifdef HAVE_IPV6 case AF_INET6: { const struct sockaddr_in6 *in = (const struct sockaddr_in6 *)inaddr; switch (si->type) { case SOCK_STREAM: type = SOCKET_TYPE_CHAR_TCP_V6; break; case SOCK_DGRAM: type = SOCKET_TYPE_CHAR_UDP_V6; break; } /* XXX no multicast/broadcast */ prt = ntohs(in->sin6_port); iface = SW_IPV6_ADDRESS; break; }#endif default: errno = ENETUNREACH; return -1; } if (bcast) *bcast = is_bcast; if (prt == 0) { /* handle auto-allocation of ephemeral ports */ for (prt = 5001; prt < 10000; prt++) { snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, socket_wrapper_dir(), type, iface, prt); if (stat(un->sun_path, &st) == 0) continue; set_port(si->family, prt, si->myname); break; } if (prt == 10000) { errno = ENFILE; return -1; } } snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT, socket_wrapper_dir(), type, iface, prt); return 0;}static struct socket_info *find_socket_info(int fd){ struct socket_info *i; for (i = sockets; i; i = i->next) { if (i->fd == fd) return i; } return NULL;}static int sockaddr_convert_to_un(struct socket_info *si, const struct sockaddr *in_addr, socklen_t in_len, struct sockaddr_un *out_addr, int alloc_sock, int *bcast){ if (!out_addr) return 0; out_addr->sun_family = AF_UNIX; switch (in_addr->sa_family) { case AF_INET:#ifdef HAVE_IPV6 case AF_INET6:#endif switch (si->type) { case SOCK_STREAM: case SOCK_DGRAM: break; default: errno = ESOCKTNOSUPPORT; return -1; } if (alloc_sock) { return convert_in_un_alloc(si, in_addr, out_addr, bcast); } else { return convert_in_un_remote(si, in_addr, out_addr, bcast); } default: break; } errno = EAFNOSUPPORT; return -1;}static int sockaddr_convert_from_un(const struct socket_info *si, const struct sockaddr_un *in_addr, socklen_t un_addrlen, int family, struct sockaddr *out_addr, socklen_t *out_addrlen){ if (out_addr == NULL || out_addrlen == NULL) return 0; if (un_addrlen == 0) { *out_addrlen = 0; return 0; } switch (family) { case AF_INET:#ifdef HAVE_IPV6 case AF_INET6:#endif switch (si->type) { case SOCK_STREAM: case SOCK_DGRAM: break; default: errno = ESOCKTNOSUPPORT; return -1; } return convert_un_in(in_addr, out_addr, out_addrlen); default: break; } errno = EAFNOSUPPORT; return -1;}enum swrap_packet_type { SWRAP_CONNECT_SEND, SWRAP_CONNECT_UNREACH, SWRAP_CONNECT_RECV, SWRAP_CONNECT_ACK, SWRAP_ACCEPT_SEND, SWRAP_ACCEPT_RECV,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -