diff options
Diffstat (limited to 'openvpn/src/openvpn/socket.h')
-rw-r--r-- | openvpn/src/openvpn/socket.h | 1088 |
1 files changed, 1088 insertions, 0 deletions
diff --git a/openvpn/src/openvpn/socket.h b/openvpn/src/openvpn/socket.h new file mode 100644 index 00000000..ef21cb61 --- /dev/null +++ b/openvpn/src/openvpn/socket.h @@ -0,0 +1,1088 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SOCKET_H +#define SOCKET_H + +#include "buffer.h" +#include "common.h" +#include "error.h" +#include "proto.h" +#include "mtu.h" +#include "win32.h" +#include "event.h" +#include "proxy.h" +#include "socks.h" +#include "misc.h" + +/* + * OpenVPN's default port number as assigned by IANA. + */ +#define OPENVPN_PORT 1194 + +/* + * Maximum size passed passed to setsockopt SNDBUF/RCVBUF + */ +#define SOCKET_SND_RCV_BUF_MAX 1000000 + +/* + * Number of seconds that "resolv-retry infinite" + * represents. + */ +#define RESOLV_RETRY_INFINITE 1000000000 + +/* + * packet_size_type is used to communicate packet size + * over the wire when stream oriented protocols are + * being used + */ + +typedef uint16_t packet_size_type; + +/* convert a packet_size_type from host to network order */ +#define htonps(x) htons(x) + +/* convert a packet_size_type from network to host order */ +#define ntohps(x) ntohs(x) + +/* OpenVPN sockaddr struct */ +struct openvpn_sockaddr +{ + /*int dummy;*/ /* add offset to force a bug if sa not explicitly dereferenced */ + union { + struct sockaddr sa; + struct sockaddr_in in4; + struct sockaddr_in6 in6; + } addr; +}; + +/* actual address of remote, based on source address of received packets */ +struct link_socket_actual +{ + /*int dummy;*/ /* add offset to force a bug if dest not explicitly dereferenced */ + struct openvpn_sockaddr dest; +#if ENABLE_IP_PKTINFO + union { +#ifdef HAVE_IN_PKTINFO + struct in_pktinfo in4; +#elif defined(IP_RECVDSTADDR) + struct in_addr in4; +#endif + struct in6_pktinfo in6; + } pi; +#endif +}; + +/* IP addresses which are persistant across SIGUSR1s */ +struct link_socket_addr +{ + struct openvpn_sockaddr local; + struct openvpn_sockaddr remote; /* initial remote */ + struct link_socket_actual actual; /* reply to this address */ +}; + +struct link_socket_info +{ + struct link_socket_addr *lsa; + bool connection_established; + const char *ipchange_command; + const struct plugin_list *plugins; + bool remote_float; + int proto; /* Protocol (PROTO_x defined below) */ + int mtu_changed; /* Set to true when mtu value is changed */ +}; + +/* + * Used to extract packets encapsulated in streams into a buffer, + * in this case IP packets embedded in a TCP stream. + */ +struct stream_buf +{ + struct buffer buf_init; + struct buffer residual; + int maxlen; + bool residual_fully_formed; + + struct buffer buf; + struct buffer next; + int len; /* -1 if not yet known */ + + bool error; /* if true, fatal TCP error has occurred, + requiring that connection be restarted */ +#if PORT_SHARE +# define PS_DISABLED 0 +# define PS_ENABLED 1 +# define PS_FOREIGN 2 + int port_share_state; +#endif +}; + +/* + * Used to set socket buffer sizes + */ +struct socket_buffer_size +{ + int rcvbuf; + int sndbuf; +}; + +/* + * This is the main socket structure used by OpenVPN. The SOCKET_ + * defines try to abstract away our implementation differences between + * using sockets on Posix vs. Win32. + */ +struct link_socket +{ + struct link_socket_info info; + + socket_descriptor_t sd; + +#ifdef ENABLE_SOCKS + socket_descriptor_t ctrl_sd; /* only used for UDP over Socks */ +#endif + +#ifdef WIN32 + struct overlapped_io reads; + struct overlapped_io writes; + struct rw_handle rw_handle; + struct rw_handle listen_handle; /* For listening on TCP socket in server mode */ +#endif + + /* used for printing status info only */ + unsigned int rwflags_debug; + + /* used for long-term queueing of pre-accepted socket listen */ + bool listen_persistent_queued; + + /* Does config file contain any <connection> ... </connection> blocks? */ + bool connection_profiles_defined; + + const char *remote_host; + int remote_port; + const char *local_host; + int local_port; + bool bind_local; + +# define INETD_NONE 0 +# define INETD_WAIT 1 +# define INETD_NOWAIT 2 + int inetd; + +# define LS_MODE_DEFAULT 0 +# define LS_MODE_TCP_LISTEN 1 +# define LS_MODE_TCP_ACCEPT_FROM 2 + int mode; + + int resolve_retry_seconds; + int connect_retry_seconds; + int connect_timeout; + int connect_retry_max; + int mtu_discover_type; + + struct socket_buffer_size socket_buffer_sizes; + + int mtu; /* OS discovered MTU, or 0 if unknown */ + + bool did_resolve_remote; + +# define SF_USE_IP_PKTINFO (1<<0) +# define SF_TCP_NODELAY (1<<1) +# define SF_PORT_SHARE (1<<2) +# define SF_HOST_RANDOMIZE (1<<3) +# define SF_GETADDRINFO_DGRAM (1<<4) + unsigned int sockflags; + + /* for stream sockets */ + struct stream_buf stream_buf; + struct buffer stream_buf_data; + bool stream_reset; + +#ifdef ENABLE_HTTP_PROXY + /* HTTP proxy */ + struct http_proxy_info *http_proxy; +#endif + +#ifdef ENABLE_SOCKS + /* Socks proxy */ + struct socks_proxy_info *socks_proxy; + struct link_socket_actual socks_relay; /* Socks UDP relay address */ +#endif + +#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_SOCKS) + /* The OpenVPN server we will use the proxy to connect to */ + const char *proxy_dest_host; + int proxy_dest_port; +#endif + +#if PASSTOS_CAPABILITY + /* used to get/set TOS. */ + uint8_t ptos; + bool ptos_defined; +#endif + +#ifdef ENABLE_DEBUG + int gremlin; /* --gremlin bits */ +#endif +}; + +/* + * Some Posix/Win32 differences. + */ + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +#ifdef WIN32 + +#define openvpn_close_socket(s) closesocket(s) + +int socket_recv_queue (struct link_socket *sock, int maxsize); + +int socket_send_queue (struct link_socket *sock, + struct buffer *buf, + const struct link_socket_actual *to); + +int socket_finalize ( + SOCKET s, + struct overlapped_io *io, + struct buffer *buf, + struct link_socket_actual *from); + +#else + +#define openvpn_close_socket(s) close(s) + +#endif + +struct link_socket *link_socket_new (void); + +void socket_bind (socket_descriptor_t sd, + struct openvpn_sockaddr *local, + const char *prefix); + +int openvpn_connect (socket_descriptor_t sd, + struct openvpn_sockaddr *remote, + int connect_timeout, + volatile int *signal_received); + +/* + * Initialize link_socket object. + */ + +void +link_socket_init_phase1 (struct link_socket *sock, + const bool connection_profiles_defined, + const char *local_host, + int local_port, + const char *remote_host, + int remote_port, + int proto, + int mode, + const struct link_socket *accept_from, +#ifdef ENABLE_HTTP_PROXY + struct http_proxy_info *http_proxy, +#endif +#ifdef ENABLE_SOCKS + struct socks_proxy_info *socks_proxy, +#endif +#ifdef ENABLE_DEBUG + int gremlin, +#endif + bool bind_local, + bool remote_float, + int inetd, + struct link_socket_addr *lsa, + const char *ipchange_command, + const struct plugin_list *plugins, + int resolve_retry_seconds, + int connect_retry_seconds, + int connect_timeout, + int connect_retry_max, + int mtu_discover_type, + int rcvbuf, + int sndbuf, + int mark, + unsigned int sockflags); + +void link_socket_init_phase2 (struct link_socket *sock, + const struct frame *frame, + volatile int *signal_received); + +void socket_adjust_frame_parameters (struct frame *frame, int proto); + +void frame_adjust_path_mtu (struct frame *frame, int pmtu, int proto); + +void link_socket_close (struct link_socket *sock); + +void sd_close (socket_descriptor_t *sd); + +#define PS_SHOW_PORT_IF_DEFINED (1<<0) +#define PS_SHOW_PORT (1<<1) +#define PS_SHOW_PKTINFO (1<<2) +#define PS_DONT_SHOW_ADDR (1<<3) + +const char *print_sockaddr_ex (const struct openvpn_sockaddr *addr, + const char* separator, + const unsigned int flags, + struct gc_arena *gc); + + +const char *print_sockaddr (const struct openvpn_sockaddr *addr, + struct gc_arena *gc); + +const char *print_link_socket_actual_ex (const struct link_socket_actual *act, + const char* separator, + const unsigned int flags, + struct gc_arena *gc); + +const char *print_link_socket_actual (const struct link_socket_actual *act, + struct gc_arena *gc); + + +#define IA_EMPTY_IF_UNDEF (1<<0) +#define IA_NET_ORDER (1<<1) +const char *print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc); +const char *print_in6_addr (struct in6_addr addr6, unsigned int flags, struct gc_arena *gc); +struct in6_addr add_in6_addr( struct in6_addr base, uint32_t add ); + +#define SA_IP_PORT (1<<0) +#define SA_SET_IF_NONZERO (1<<1) +void setenv_sockaddr (struct env_set *es, + const char *name_prefix, + const struct openvpn_sockaddr *addr, + const unsigned int flags); + +void setenv_in_addr_t (struct env_set *es, + const char *name_prefix, + in_addr_t addr, + const unsigned int flags); + +void setenv_link_socket_actual (struct env_set *es, + const char *name_prefix, + const struct link_socket_actual *act, + const unsigned int flags); + +void bad_address_length (int actual, int expected); + +/* IPV4_INVALID_ADDR: returned by link_socket_current_remote() + * to ease redirect-gateway logic for ipv4 tunnels on ipv6 endpoints + */ +#define IPV4_INVALID_ADDR 0xffffffff +in_addr_t link_socket_current_remote (const struct link_socket_info *info); + +void link_socket_connection_initiated (const struct buffer *buf, + struct link_socket_info *info, + const struct link_socket_actual *addr, + const char *common_name, + struct env_set *es); + +void link_socket_bad_incoming_addr (struct buffer *buf, + const struct link_socket_info *info, + const struct link_socket_actual *from_addr); + +void link_socket_bad_outgoing_addr (void); + +void setenv_trusted (struct env_set *es, const struct link_socket_info *info); + +bool link_socket_update_flags (struct link_socket *ls, unsigned int sockflags); +void link_socket_update_buffer_sizes (struct link_socket *ls, int rcvbuf, int sndbuf); + +/* + * Low-level functions + */ + +/* return values of openvpn_inet_aton */ +#define OIA_HOSTNAME 0 +#define OIA_IP 1 +#define OIA_ERROR -1 +int openvpn_inet_aton (const char *dotted_quad, struct in_addr *addr); + +/* integrity validation on pulled options */ +bool ip_addr_dotted_quad_safe (const char *dotted_quad); +bool ip_or_dns_addr_safe (const char *addr, const bool allow_fqdn); +bool mac_addr_safe (const char *mac_addr); +bool ipv6_addr_safe (const char *ipv6_text_addr); + +socket_descriptor_t create_socket_tcp (void); + +socket_descriptor_t socket_do_accept (socket_descriptor_t sd, + struct link_socket_actual *act, + const bool nowait); +/* + * proto related + */ +bool proto_is_net(int proto); +bool proto_is_dgram(int proto); +bool proto_is_udp(int proto); +bool proto_is_tcp(int proto); + + +#if UNIX_SOCK_SUPPORT + +socket_descriptor_t create_socket_unix (void); + +void socket_bind_unix (socket_descriptor_t sd, + struct sockaddr_un *local, + const char *prefix); + +socket_descriptor_t socket_accept_unix (socket_descriptor_t sd, + struct sockaddr_un *remote); + +int socket_connect_unix (socket_descriptor_t sd, + struct sockaddr_un *remote); + +void sockaddr_unix_init (struct sockaddr_un *local, const char *path); + +const char *sockaddr_unix_name (const struct sockaddr_un *local, const char *null); + +void socket_delete_unix (const struct sockaddr_un *local); + +bool unix_socket_get_peer_uid_gid (const socket_descriptor_t sd, int *uid, int *gid); + +#endif + +/* + * DNS resolution + */ + +struct resolve_list { + int len; + in_addr_t data[16]; +}; + +#define GETADDR_RESOLVE (1<<0) +#define GETADDR_FATAL (1<<1) +#define GETADDR_HOST_ORDER (1<<2) +#define GETADDR_MENTION_RESOLVE_RETRY (1<<3) +#define GETADDR_FATAL_ON_SIGNAL (1<<4) +#define GETADDR_WARN_ON_SIGNAL (1<<5) +#define GETADDR_MSG_VIRT_OUT (1<<6) +#define GETADDR_TRY_ONCE (1<<7) +#define GETADDR_UPDATE_MANAGEMENT_STATE (1<<8) +#define GETADDR_RANDOMIZE (1<<9) + +/* [ab]use flags bits to get socktype info downstream */ +/* TODO(jjo): resolve tradeoff between hackiness|args-overhead */ +#define GETADDR_DGRAM (1<<10) +#define dnsflags_to_socktype(flags) ((flags & GETADDR_DGRAM) ? SOCK_DGRAM : SOCK_STREAM) + +in_addr_t getaddr (unsigned int flags, + const char *hostname, + int resolve_retry_seconds, + bool *succeeded, + volatile int *signal_received); + +in_addr_t getaddr_multi (unsigned int flags, + const char *hostname, + int resolve_retry_seconds, + bool *succeeded, + volatile int *signal_received, + struct resolve_list *reslist); + +/* + * Transport protocol naming and other details. + */ + +/* + * Use enum's instead of #define to allow for easier + * optional proto support + */ +enum proto_num { + PROTO_NONE, /* catch for uninitialized */ + PROTO_UDPv4, + PROTO_TCPv4_SERVER, + PROTO_TCPv4_CLIENT, + PROTO_TCPv4, + PROTO_UDPv6, + PROTO_TCPv6_SERVER, + PROTO_TCPv6_CLIENT, + PROTO_TCPv6, + PROTO_N +}; + +int ascii2proto (const char* proto_name); +const char *proto2ascii (int proto, bool display_form); +const char *proto2ascii_all (struct gc_arena *gc); +int proto_remote (int proto, bool remote); +const char *addr_family_name(int af); + +/* + * Overhead added to packets by various protocols. + */ +#define IPv4_UDP_HEADER_SIZE 28 +#define IPv4_TCP_HEADER_SIZE 40 +#define IPv6_UDP_HEADER_SIZE 48 +#define IPv6_TCP_HEADER_SIZE 60 + +extern const int proto_overhead[]; + +static inline int +datagram_overhead (int proto) +{ + ASSERT (proto >= 0 && proto < PROTO_N); + return proto_overhead [proto]; +} + +/* + * Misc inline functions + */ + +static inline bool +legal_ipv4_port (int port) +{ + return port > 0 && port < 65536; +} + +static inline bool +link_socket_proto_connection_oriented (int proto) +{ + return !proto_is_dgram(proto); +} + +static inline bool +link_socket_connection_oriented (const struct link_socket *sock) +{ + if (sock) + return link_socket_proto_connection_oriented (sock->info.proto); + else + return false; +} + +static inline bool +addr_defined (const struct openvpn_sockaddr *addr) +{ + if (!addr) return 0; + switch (addr->addr.sa.sa_family) { + case AF_INET: return addr->addr.in4.sin_addr.s_addr != 0; + case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&addr->addr.in6.sin6_addr); + default: return 0; + } +} +static inline bool +addr_defined_ipi (const struct link_socket_actual *lsa) +{ +#if ENABLE_IP_PKTINFO + if (!lsa) return 0; + switch (lsa->dest.addr.sa.sa_family) { +#ifdef HAVE_IN_PKTINFO + case AF_INET: return lsa->pi.in4.ipi_spec_dst.s_addr != 0; +#elif defined(IP_RECVDSTADDR) + case AF_INET: return lsa->pi.in4.s_addr != 0; +#endif + case AF_INET6: return !IN6_IS_ADDR_UNSPECIFIED(&lsa->pi.in6.ipi6_addr); + default: return 0; + } +#else + ASSERT(0); +#endif + return false; +} + +static inline bool +link_socket_actual_defined (const struct link_socket_actual *act) +{ + return act && addr_defined (&act->dest); +} + +static inline bool +addr_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2) +{ + switch(a1->addr.sa.sa_family) { + case AF_INET: + return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr; + case AF_INET6: + return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr); + } + ASSERT(0); + return false; +} + +static inline in_addr_t +addr_host (const struct openvpn_sockaddr *addr) +{ + /* + * "public" addr returned is checked against ifconfig for + * possible clash: non sense for now given + * that we do ifconfig only IPv4 + */ + if(addr->addr.sa.sa_family != AF_INET) + return 0; + return ntohl (addr->addr.in4.sin_addr.s_addr); +} + +static inline bool +addr_port_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2) +{ + switch(a1->addr.sa.sa_family) { + case AF_INET: + return a1->addr.in4.sin_addr.s_addr == a2->addr.in4.sin_addr.s_addr + && a1->addr.in4.sin_port == a2->addr.in4.sin_port; + case AF_INET6: + return IN6_ARE_ADDR_EQUAL(&a1->addr.in6.sin6_addr, &a2->addr.in6.sin6_addr) + && a1->addr.in6.sin6_port == a2->addr.in6.sin6_port; + } + ASSERT(0); + return false; +} + +static inline bool +addr_match_proto (const struct openvpn_sockaddr *a1, + const struct openvpn_sockaddr *a2, + const int proto) +{ + return link_socket_proto_connection_oriented (proto) + ? addr_match (a1, a2) + : addr_port_match (a1, a2); +} + +static inline void +addr_zero_host(struct openvpn_sockaddr *addr) +{ + switch(addr->addr.sa.sa_family) { + case AF_INET: + addr->addr.in4.sin_addr.s_addr = 0; + break; + case AF_INET6: + memset(&addr->addr.in6.sin6_addr, 0, sizeof (struct in6_addr)); + break; + } +} + +static inline void +addr_copy_sa(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src) +{ + dst->addr = src->addr; +} + +static inline void +addr_copy_host(struct openvpn_sockaddr *dst, const struct openvpn_sockaddr *src) +{ + switch(src->addr.sa.sa_family) { + case AF_INET: + dst->addr.in4.sin_addr.s_addr = src->addr.in4.sin_addr.s_addr; + break; + case AF_INET6: + dst->addr.in6.sin6_addr = src->addr.in6.sin6_addr; + break; + } +} + +static inline bool +addr_inet4or6(struct sockaddr *addr) +{ + return addr->sa_family == AF_INET || addr->sa_family == AF_INET6; +} + +int addr_guess_family(int proto, const char *name); +static inline int +af_addr_size(unsigned short af) +{ + switch(af) { + case AF_INET: return sizeof (struct sockaddr_in); + case AF_INET6: return sizeof (struct sockaddr_in6); + default: +#if 0 + /* could be called from socket_do_accept() with empty addr */ + msg (M_ERR, "Bad address family: %d\n", af); + ASSERT(0); +#endif + return 0; + } +} + +static inline bool +link_socket_actual_match (const struct link_socket_actual *a1, const struct link_socket_actual *a2) +{ + return addr_port_match (&a1->dest, &a2->dest); +} + +#if PORT_SHARE + +static inline bool +socket_foreign_protocol_detected (const struct link_socket *sock) +{ + return link_socket_connection_oriented (sock) + && sock->stream_buf.port_share_state == PS_FOREIGN; +} + +static inline const struct buffer * +socket_foreign_protocol_head (const struct link_socket *sock) +{ + return &sock->stream_buf.buf; +} + +static inline int +socket_foreign_protocol_sd (const struct link_socket *sock) +{ + return sock->sd; +} + +#endif + +static inline bool +socket_connection_reset (const struct link_socket *sock, int status) +{ + if (link_socket_connection_oriented (sock)) + { + if (sock->stream_reset || sock->stream_buf.error) + return true; + else if (status < 0) + { + const int err = openvpn_errno_socket (); +#ifdef WIN32 + return err == WSAECONNRESET || err == WSAECONNABORTED; +#else + return err == ECONNRESET; +#endif + } + } + return false; +} + +static inline bool +link_socket_verify_incoming_addr (struct buffer *buf, + const struct link_socket_info *info, + const struct link_socket_actual *from_addr) +{ + if (buf->len > 0) + { + switch (from_addr->dest.addr.sa.sa_family) { + case AF_INET6: + case AF_INET: + if (!link_socket_actual_defined (from_addr)) + return false; + if (info->remote_float || !addr_defined (&info->lsa->remote)) + return true; + if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto)) + return true; + } + } + return false; +} + +static inline void +link_socket_get_outgoing_addr (struct buffer *buf, + const struct link_socket_info *info, + struct link_socket_actual **act) +{ + if (buf->len > 0) + { + struct link_socket_addr *lsa = info->lsa; + if (link_socket_actual_defined (&lsa->actual)) + *act = &lsa->actual; + else + { + link_socket_bad_outgoing_addr (); + buf->len = 0; + *act = NULL; + } + } +} + +static inline void +link_socket_set_outgoing_addr (const struct buffer *buf, + struct link_socket_info *info, + const struct link_socket_actual *act, + const char *common_name, + struct env_set *es) +{ + if (!buf || buf->len > 0) + { + struct link_socket_addr *lsa = info->lsa; + if ( + /* new or changed address? */ + (!info->connection_established + || !addr_match_proto (&act->dest, &lsa->actual.dest, info->proto)) + /* address undef or address == remote or --float */ + && (info->remote_float + || !addr_defined (&lsa->remote) + || addr_match_proto (&act->dest, &lsa->remote, info->proto)) + ) + { + link_socket_connection_initiated (buf, info, act, common_name, es); + } + } +} + +/* + * Stream buffer handling -- stream_buf is a helper class + * to assist in the packetization of stream transport protocols + * such as TCP. + */ + +void stream_buf_init (struct stream_buf *sb, + struct buffer *buf, + const unsigned int sockflags, + const int proto); + +void stream_buf_close (struct stream_buf* sb); +bool stream_buf_added (struct stream_buf *sb, int length_added); + +static inline bool +stream_buf_read_setup (struct link_socket* sock) +{ + bool stream_buf_read_setup_dowork (struct link_socket* sock); + if (link_socket_connection_oriented (sock)) + return stream_buf_read_setup_dowork (sock); + else + return true; +} + +/* + * Socket Read Routines + */ + +int link_socket_read_tcp (struct link_socket *sock, + struct buffer *buf); + +#ifdef WIN32 + +static inline int +link_socket_read_udp_win32 (struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *from) +{ + return socket_finalize (sock->sd, &sock->reads, buf, from); +} + +#else + +int link_socket_read_udp_posix (struct link_socket *sock, + struct buffer *buf, + int maxsize, + struct link_socket_actual *from); + +#endif + +/* read a TCP or UDP packet from link */ +static inline int +link_socket_read (struct link_socket *sock, + struct buffer *buf, + int maxsize, + struct link_socket_actual *from) +{ + if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */ + { + int res; + +#ifdef WIN32 + res = link_socket_read_udp_win32 (sock, buf, from); +#else + res = link_socket_read_udp_posix (sock, buf, maxsize, from); +#endif + return res; + } + else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */ + { + /* from address was returned by accept */ + addr_copy_sa(&from->dest, &sock->info.lsa->actual.dest); + return link_socket_read_tcp (sock, buf); + } + else + { + ASSERT (0); + return -1; /* NOTREACHED */ + } +} + +/* + * Socket Write routines + */ + +int link_socket_write_tcp (struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to); + +#ifdef WIN32 + +static inline int +link_socket_write_win32 (struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ + int err = 0; + int status = 0; + if (overlapped_io_active (&sock->writes)) + { + status = socket_finalize (sock->sd, &sock->writes, NULL, NULL); + if (status < 0) + err = WSAGetLastError (); + } + socket_send_queue (sock, buf, to); + if (status < 0) + { + WSASetLastError (err); + return status; + } + else + return BLEN (buf); +} + +#else + +static inline int +link_socket_write_udp_posix (struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ +#if ENABLE_IP_PKTINFO + int link_socket_write_udp_posix_sendmsg (struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to); + + if (proto_is_udp(sock->info.proto) && (sock->sockflags & SF_USE_IP_PKTINFO) + && addr_defined_ipi(to)) + return link_socket_write_udp_posix_sendmsg (sock, buf, to); + else +#endif + return sendto (sock->sd, BPTR (buf), BLEN (buf), 0, + (struct sockaddr *) &to->dest.addr.sa, + (socklen_t) af_addr_size(to->dest.addr.sa.sa_family)); +} + +static inline int +link_socket_write_tcp_posix (struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ + return send (sock->sd, BPTR (buf), BLEN (buf), MSG_NOSIGNAL); +} + +#endif + +static inline int +link_socket_write_udp (struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ +#ifdef WIN32 + return link_socket_write_win32 (sock, buf, to); +#else + return link_socket_write_udp_posix (sock, buf, to); +#endif +} + +/* write a TCP or UDP packet to link */ +static inline int +link_socket_write (struct link_socket *sock, + struct buffer *buf, + struct link_socket_actual *to) +{ + if (proto_is_udp(sock->info.proto)) /* unified UDPv4 and UDPv6 */ + { + return link_socket_write_udp (sock, buf, to); + } + else if (proto_is_tcp(sock->info.proto)) /* unified TCPv4 and TCPv6 */ + { + return link_socket_write_tcp (sock, buf, to); + } + else + { + ASSERT (0); + return -1; /* NOTREACHED */ + } +} + +#if PASSTOS_CAPABILITY + +/* + * Extract TOS bits. Assumes that ipbuf is a valid IPv4 packet. + */ +static inline void +link_socket_extract_tos (struct link_socket *ls, const struct buffer *ipbuf) +{ + if (ls && ipbuf) + { + struct openvpn_iphdr *iph = (struct openvpn_iphdr *) BPTR (ipbuf); + ls->ptos = iph->tos; + ls->ptos_defined = true; + } +} + +/* + * Set socket properties to reflect TOS bits which were extracted + * from tunnel packet. + */ +static inline void +link_socket_set_tos (struct link_socket *ls) +{ + if (ls && ls->ptos_defined) + setsockopt (ls->sd, IPPROTO_IP, IP_TOS, &ls->ptos, sizeof (ls->ptos)); +} + +#endif + +/* + * Socket I/O wait functions + */ + +static inline bool +socket_read_residual (const struct link_socket *s) +{ + return s && s->stream_buf.residual_fully_formed; +} + +static inline event_t +socket_event_handle (const struct link_socket *s) +{ +#ifdef WIN32 + return &s->rw_handle; +#else + return s->sd; +#endif +} + +event_t socket_listen_event_handle (struct link_socket *s); + +unsigned int +socket_set (struct link_socket *s, + struct event_set *es, + unsigned int rwflags, + void *arg, + unsigned int *persistent); + +static inline void +socket_set_listen_persistent (struct link_socket *s, + struct event_set *es, + void *arg) +{ + if (s && !s->listen_persistent_queued) + { + event_ctl (es, socket_listen_event_handle (s), EVENT_READ, arg); + s->listen_persistent_queued = true; + } +} + +static inline void +socket_reset_listen_persistent (struct link_socket *s) +{ +#ifdef WIN32 + reset_net_event_win32 (&s->listen_handle, s->sd); +#endif +} + +const char *socket_stat (const struct link_socket *s, unsigned int rwflags, struct gc_arena *gc); + +#endif /* SOCKET_H */ |