1 /*
2  * Copyright (c) 2002-2019 Balabit
3  * Copyright (c) 1998-2019 Balázs Scheidler
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * As an additional exemption you are allowed to compile & link against the
20  * OpenSSL libraries as published by the OpenSSL project. See the file
21  * COPYING for details.
22  *
23  */
24 #include "transport-udp-socket.h"
25 #include "transport/transport-socket.h"
26 #include "gsocket.h"
27 #include "scratch-buffers.h"
28 #include "str-format.h"
29 
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <errno.h>
33 
34 typedef struct _LogTransportUDP LogTransportUDP;
35 struct _LogTransportUDP
36 {
37   LogTransportSocket super;
38   GSockAddr *bind_addr;
39 };
40 
41 #if defined(SYSLOG_NG_HAVE_CTRLBUF_IN_MSGHDR)
42 
43 #if defined(__DragonFly__) || defined(__OpenBSD__)
44 
45 GSockAddr *
_extract_dest_ip4_addr_from_cmsg(struct cmsghdr * cmsg,GSockAddr * bind_addr)46 _extract_dest_ip4_addr_from_cmsg(struct cmsghdr *cmsg, GSockAddr *bind_addr)
47 {
48   if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR)
49     {
50       struct sockaddr_in sin = *(struct sockaddr_in *) &bind_addr->sa;
51 
52       struct in_addr *sin_addr = (struct in_addr *) CMSG_DATA(cmsg);
53       sin.sin_addr = *sin_addr;
54 
55       return g_sockaddr_new((struct sockaddr *) &sin, sizeof(sin));
56     }
57   return NULL;
58 }
59 
60 #else
61 
62 GSockAddr *
_extract_dest_ip4_addr_from_cmsg(struct cmsghdr * cmsg,GSockAddr * bind_addr)63 _extract_dest_ip4_addr_from_cmsg(struct cmsghdr *cmsg, GSockAddr *bind_addr)
64 {
65   if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO)
66     {
67       struct sockaddr_in sin;
68       struct in_pktinfo *inpkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
69 
70       /* we need to copy the port number from the bind address as it is not
71        * part of IP_PKTINFO */
72 
73       sin = *(struct sockaddr_in *) &bind_addr->sa;
74       sin.sin_addr = inpkt->ipi_addr;
75       return g_sockaddr_new((struct sockaddr *) &sin, sizeof(sin));
76     }
77   return NULL;
78 }
79 #endif
80 
81 #if SYSLOG_NG_ENABLE_IPV6
82 
83 GSockAddr *
_extract_dest_ip6_addr_from_cmsg(struct cmsghdr * cmsg,GSockAddr * bind_addr)84 _extract_dest_ip6_addr_from_cmsg(struct cmsghdr *cmsg, GSockAddr *bind_addr)
85 {
86   if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO)
87     {
88       struct sockaddr_in6 sin6;
89       struct in6_pktinfo *in6pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
90 
91       /* we need to copy the port number (and scope id) from the bind
92        * address as it is not part of IPV6_PKTINFO */
93 
94       sin6 = *(struct sockaddr_in6 *) &bind_addr->sa;
95       sin6.sin6_addr = in6pkt->ipi6_addr;
96       return g_sockaddr_new((struct sockaddr *) &sin6, sizeof(sin6));
97     }
98   return NULL;
99 }
100 
101 #endif
102 
103 GSockAddr *
_extract_dest_addr_from_cmsg(struct cmsghdr * cmsg,GSockAddr * bind_addr)104 _extract_dest_addr_from_cmsg(struct cmsghdr *cmsg, GSockAddr *bind_addr)
105 {
106   if (bind_addr->sa.sa_family == AF_INET)
107     return _extract_dest_ip4_addr_from_cmsg(cmsg, bind_addr);
108 #if SYSLOG_NG_ENABLE_IPV6
109   else if (bind_addr->sa.sa_family == AF_INET6)
110     return _extract_dest_ip6_addr_from_cmsg(cmsg, bind_addr);
111 #endif
112   else
113     g_assert_not_reached();
114 }
115 
116 static void
_feed_aux_from_cmsg(LogTransportUDP * self,LogTransportAuxData * aux,struct msghdr * msg)117 _feed_aux_from_cmsg(LogTransportUDP *self, LogTransportAuxData *aux, struct msghdr *msg)
118 {
119   struct cmsghdr *cmsg;
120 
121   for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
122     {
123       GSockAddr *dest_addr;
124 
125       dest_addr = _extract_dest_addr_from_cmsg(cmsg, self->bind_addr);
126       if (dest_addr)
127         {
128           log_transport_aux_data_set_local_addr_ref(aux, dest_addr);
129           break;
130         }
131     }
132 }
133 
134 #else
135 #define _feed_aux_from_cmsg(self, aux, msg)
136 #endif
137 
138 
139 static gssize
log_transport_udp_socket_read_method(LogTransport * s,gpointer buf,gsize buflen,LogTransportAuxData * aux)140 log_transport_udp_socket_read_method(LogTransport *s, gpointer buf, gsize buflen, LogTransportAuxData *aux)
141 {
142   LogTransportUDP *self = (LogTransportUDP *) s;
143   gint rc;
144   struct msghdr msg;
145   struct iovec iov[1];
146   struct sockaddr_storage ss;
147 #if defined(SYSLOG_NG_HAVE_CTRLBUF_IN_MSGHDR)
148   gchar ctlbuf[64];
149   msg.msg_control = ctlbuf;
150   msg.msg_controllen = sizeof(ctlbuf);
151 #endif
152 
153   msg.msg_name = (struct sockaddr *) &ss;
154   msg.msg_namelen = sizeof(ss);
155   msg.msg_iovlen = 1;
156   msg.msg_iov = iov;
157   iov[0].iov_base = buf;
158   iov[0].iov_len = buflen;
159 
160   do
161     {
162       rc = recvmsg(self->super.super.fd, &msg, 0);
163     }
164   while (rc == -1 && errno == EINTR);
165 
166   if (rc == 0)
167     {
168       /* DGRAM sockets should never return EOF, they just need to be read again */
169       rc = -1;
170       errno = EAGAIN;
171     }
172   else if (rc > 0)
173     {
174       if (msg.msg_namelen && aux)
175         log_transport_aux_data_set_peer_addr_ref(aux, g_sockaddr_new((struct sockaddr *) &ss, msg.msg_namelen));
176       if (aux)
177         aux->proto = self->super.proto;
178       _feed_aux_from_cmsg(self, aux, &msg);
179     }
180   return rc;
181 
182 }
183 
184 static void
log_transport_udp_setup_fd(LogTransportUDP * self,gint fd)185 log_transport_udp_setup_fd(LogTransportUDP *self, gint fd)
186 {
187   gint on = 1;
188 
189   self->bind_addr = g_socket_get_local_name(fd);
190 
191   if (self->super.address_family == AF_INET)
192     {
193 #if defined(__DragonFly__) || defined(__OpenBSD__)
194       setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
195 #else
196       setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
197 #endif
198     }
199 #if SYSLOG_NG_ENABLE_IPV6
200   else if (self->bind_addr->sa.sa_family == AF_INET6)
201     setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));
202 #endif
203   else
204     g_assert_not_reached();
205 
206 }
207 
208 static void
log_transport_udp_socket_free(LogTransport * s)209 log_transport_udp_socket_free(LogTransport *s)
210 {
211   LogTransportUDP *self = (LogTransportUDP *)s;
212   g_sockaddr_unref(self->bind_addr);
213   log_transport_free_method(s);
214 }
215 
216 LogTransport *
log_transport_udp_socket_new(gint fd)217 log_transport_udp_socket_new(gint fd)
218 {
219   LogTransportUDP *self = g_new0(LogTransportUDP, 1);
220 
221   log_transport_dgram_socket_init_instance(&self->super, fd);
222   self->super.super.read = log_transport_udp_socket_read_method;
223   self->super.super.free_fn = log_transport_udp_socket_free;
224 
225   log_transport_udp_setup_fd(self, fd);
226   return &self->super.super;
227 }
228