1 /*
2  * Copyright (C) 2010 Jens Georg <mail@jensge.org>
3  *
4  * Author: Jens Georg <mail@jensge.org>
5  *
6  * SPDX-License-Identifier: LGPL-2.1-or-later
7  *
8  */
9 
10 #include <config.h>
11 
12 #ifdef __APPLE__
13 #define __APPLE_USE_RFC_3542
14 #endif
15 
16 #include "gssdp-error.h"
17 #include "gssdp-socket-functions.h"
18 #include "gssdp-pktinfo-message.h"
19 
20 #include <errno.h>
21 #include <string.h>
22 
23 #include <glib.h>
24 
25 #ifdef G_OS_WIN32
26     #include <winsock2.h>
27     #include <ws2tcpip.h>
28     typedef int socklen_t;
29 #else
30     #include <sys/socket.h>
31     #include <sys/types.h>
32     #include <netinet/in.h>
33     #include <arpa/inet.h>
34 #endif
35 
36 #include "gssdp-pktinfo6-message.h"
37 static char*
gssdp_socket_error_message(int error)38 gssdp_socket_error_message (int error) {
39 #ifdef G_OS_WIN32
40         return g_win32_error_message (error);
41 #else
42         return g_strdup (g_strerror (error));
43 #endif
44 }
45 
46 static int
gssdp_socket_errno(void)47 gssdp_socket_errno (void) {
48 #ifdef G_OS_WIN32
49         return WSAGetLastError ();
50 #else
51         return errno;
52 #endif
53 }
54 
55 
56 static gboolean
gssdp_socket_option_set(GSocket * socket,int level,int option,const void * optval,socklen_t optlen,GError ** error)57 gssdp_socket_option_set (GSocket    *socket,
58                          int         level,
59                          int         option,
60                          const void *optval,
61                          socklen_t   optlen,
62                          GError    **error) {
63         int res;
64 
65         res = setsockopt (g_socket_get_fd (socket),
66                           level,
67                           option,
68                           optval,
69                           optlen);
70 
71         if (res == -1) {
72                 char *message;
73                 int error_code;
74 
75                 error_code = gssdp_socket_errno ();
76                 message = gssdp_socket_error_message (error_code);
77                 g_set_error_literal (error,
78                                      GSSDP_ERROR,
79                                      GSSDP_ERROR_FAILED,
80                                      message);
81                 g_free (message);
82         }
83 
84         return res != -1;
85 }
86 
87 gboolean
gssdp_socket_mcast_interface_set(GSocket * socket,GInetAddress * iface_address,gint index,GError ** error)88 gssdp_socket_mcast_interface_set (GSocket      *socket,
89                                   GInetAddress *iface_address,
90                                   gint          index,
91                                   GError      **error) {
92 
93         const guint8 *address;
94         gsize native_size;
95 
96         if (g_inet_address_get_family (iface_address) == G_SOCKET_FAMILY_IPV6) {
97                 return gssdp_socket_option_set (socket,
98                                                 IPPROTO_IPV6,
99                                                 IPV6_MULTICAST_IF,
100                                                 (char *)&index,
101                                                 sizeof (index),
102                                                 error);
103         } else {
104                 address = g_inet_address_to_bytes (iface_address);
105                 native_size = g_inet_address_get_native_size (iface_address);
106 
107                 return gssdp_socket_option_set (socket,
108                                                 IPPROTO_IP,
109                                                 IP_MULTICAST_IF,
110                                                 (char *) address,
111                                                 native_size,
112                                                 error);
113         }
114 }
115 
116 #define __GSSDP_UNUSED(x) (void)(x)
117 
118 gboolean
gssdp_socket_reuse_address(GSocket * socket,gboolean enable,GError ** error)119 gssdp_socket_reuse_address (GSocket *socket,
120                             gboolean enable,
121                             GError **error) {
122 #if defined(G_OS_WIN32) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
123         return gssdp_socket_option_set (socket,
124                                         SOL_SOCKET,
125 #if defined(__OpenBSD__) || defined (__NetBSD__) || defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
126                                         SO_REUSEPORT,
127 #else
128                                         SO_REUSEADDR,
129 #endif
130 
131                                         (char *) &enable,
132                                         sizeof (enable),
133                                         error);
134 #else
135         __GSSDP_UNUSED(socket);
136         __GSSDP_UNUSED(enable);
137         __GSSDP_UNUSED(error);
138 #endif
139         return TRUE;
140 }
141 
142 gboolean
gssdp_socket_enable_info(GSocket * socket,GSocketFamily family,gboolean enable,GError ** error)143 gssdp_socket_enable_info         (GSocket *socket,
144                                   GSocketFamily family,
145                                   gboolean enable,
146                                   GError **error)
147 {
148 #ifdef HAVE_PKTINFO
149         /* Register the type so g_socket_control_message_deserialize() will
150          * find it */
151         g_type_ensure (GSSDP_TYPE_PKTINFO_MESSAGE);
152         g_type_ensure (GSSDP_TYPE_PKTINFO6_MESSAGE);
153 
154         if (family == G_SOCKET_FAMILY_IPV6) {
155                 return gssdp_socket_option_set (socket,
156                                                 IPPROTO_IPV6,
157                                                 IPV6_RECVPKTINFO,
158                                                 (char *)&enable,
159                                                 sizeof (enable),
160                                                 error);
161         } else if (family == G_SOCKET_FAMILY_IPV4) {
162                 return gssdp_socket_option_set (socket,
163                                                 IPPROTO_IP,
164                                                 IP_PKTINFO,
165                                                 (char *) &enable,
166                                                 sizeof (enable),
167                                                 error);
168         } else {
169                 g_warning ("Invalid socket family: %d", family);
170 
171                 return FALSE;
172         }
173 #else
174     __GSSDP_UNUSED (socket);
175     __GSSDP_UNUSED (enable);
176     __GSSDP_UNUSED (error);
177 
178     return TRUE;
179 #endif
180 }
181