1/* $Id: upnputils.c,v 1.10 2014/11/07 11:53:39 nanard Exp $ */
2/* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2014 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
7
8#include "config.h"
9
10#include <stdio.h>
11#include <string.h>
12#include <syslog.h>
13#include <unistd.h>
14#include <fcntl.h>
15#include <sys/types.h>
16#include <sys/socket.h>
17#include <netinet/in.h>
18#include <arpa/inet.h>
19#ifdef AF_LINK
20#include <net/if_dl.h>
21#endif
22#include <errno.h>
23
24#include "upnputils.h"
25#include "upnpglobalvars.h"
26#ifdef ENABLE_IPV6
27#include "getroute.h"
28#endif
29
30int
31sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size)
32{
33	char buffer[64];
34	unsigned short port = 0;
35	int n = -1;
36
37	switch(addr->sa_family)
38	{
39#ifdef AF_INET6
40	case AF_INET6:
41		if(inet_ntop(addr->sa_family,
42		             &((struct sockaddr_in6 *)addr)->sin6_addr,
43		             buffer, sizeof(buffer)) == NULL) {
44			snprintf(buffer, sizeof(buffer), "inet_ntop: %s", strerror(errno));
45		}
46		port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
47		if(((struct sockaddr_in6 *)addr)->sin6_scope_id > 0) {
48			char ifname[IF_NAMESIZE];
49			if(if_indextoname(((struct sockaddr_in6 *)addr)->sin6_scope_id, ifname) == NULL)
50				strncpy(ifname, "ERROR", sizeof(ifname));
51			n = snprintf(str, size, "[%s%%%s]:%hu", buffer, ifname, port);
52		} else {
53			n = snprintf(str, size, "[%s]:%hu", buffer, port);
54		}
55		break;
56#endif /* AF_INET6 */
57	case AF_INET:
58		if(inet_ntop(addr->sa_family,
59		             &((struct sockaddr_in *)addr)->sin_addr,
60		             buffer, sizeof(buffer)) == NULL) {
61			snprintf(buffer, sizeof(buffer), "inet_ntop: %s", strerror(errno));
62		}
63		port = ntohs(((struct sockaddr_in *)addr)->sin_port);
64		n = snprintf(str, size, "%s:%hu", buffer, port);
65		break;
66#ifdef AF_LINK
67#if defined(__sun)
68		/* solaris does not seem to have link_ntoa */
69		/* #define link_ntoa _link_ntoa	*/
70#define link_ntoa(x) "dummy-link_ntoa"
71#endif
72	case AF_LINK:
73		{
74			struct sockaddr_dl * sdl = (struct sockaddr_dl *)addr;
75			n = snprintf(str, size, "index=%hu type=%d %s",
76			             sdl->sdl_index, sdl->sdl_type,
77			             link_ntoa(sdl));
78		}
79		break;
80#endif	/* AF_LINK */
81	default:
82		n = snprintf(str, size, "unknown address family %d", addr->sa_family);
83#if 0
84		n = snprintf(str, size, "unknown address family %d "
85		             "%02x %02x %02x %02x %02x %02x %02x %02x",
86		             addr->sa_family,
87		             addr->sa_data[0], addr->sa_data[1], (unsigned)addr->sa_data[2], addr->sa_data[3],
88		             addr->sa_data[4], addr->sa_data[5], (unsigned)addr->sa_data[6], addr->sa_data[7]);
89#endif
90	}
91	return n;
92}
93
94
95int
96set_non_blocking(int fd)
97{
98	int flags = fcntl(fd, F_GETFL);
99	if(flags < 0)
100		return 0;
101	if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
102		return 0;
103	return 1;
104}
105
106struct lan_addr_s *
107get_lan_for_peer(const struct sockaddr * peer)
108{
109	struct lan_addr_s * lan_addr = NULL;
110#ifdef DEBUG
111	char dbg_str[64];
112#endif /* DEBUG */
113
114#ifdef ENABLE_IPV6
115	if(peer->sa_family == AF_INET6)
116	{
117		struct sockaddr_in6 * peer6 = (struct sockaddr_in6 *)peer;
118		if(IN6_IS_ADDR_V4MAPPED(&peer6->sin6_addr))
119		{
120			struct in_addr peer_addr;
121			memcpy(&peer_addr, &peer6->sin6_addr.s6_addr[12], 4);
122			for(lan_addr = lan_addrs.lh_first;
123			    lan_addr != NULL;
124			    lan_addr = lan_addr->list.le_next)
125			{
126				if( (peer_addr.s_addr & lan_addr->mask.s_addr)
127				   == (lan_addr->addr.s_addr & lan_addr->mask.s_addr))
128					break;
129			}
130		}
131		else
132		{
133			int index = -1;
134			if(peer6->sin6_scope_id > 0)
135				index = (int)peer6->sin6_scope_id;
136			else
137			{
138				if(get_src_for_route_to(peer, NULL, NULL, &index) < 0)
139					return NULL;
140			}
141			syslog(LOG_DEBUG, "%s looking for LAN interface index=%d",
142			       "get_lan_for_peer()", index);
143			for(lan_addr = lan_addrs.lh_first;
144			    lan_addr != NULL;
145			    lan_addr = lan_addr->list.le_next)
146			{
147				syslog(LOG_DEBUG,
148				       "ifname=%s index=%u str=%s addr=%08x mask=%08x",
149				       lan_addr->ifname, lan_addr->index,
150				       lan_addr->str,
151				       ntohl(lan_addr->addr.s_addr),
152				       ntohl(lan_addr->mask.s_addr));
153				if(index == (int)lan_addr->index)
154					break;
155			}
156		}
157	}
158	else if(peer->sa_family == AF_INET)
159	{
160#endif /* ENABLE_IPV6 */
161		for(lan_addr = lan_addrs.lh_first;
162		    lan_addr != NULL;
163		    lan_addr = lan_addr->list.le_next)
164		{
165			if( (((const struct sockaddr_in *)peer)->sin_addr.s_addr & lan_addr->mask.s_addr)
166			   == (lan_addr->addr.s_addr & lan_addr->mask.s_addr))
167				break;
168		}
169#ifdef ENABLE_IPV6
170	}
171#endif /* ENABLE_IPV6 */
172
173#ifdef DEBUG
174	sockaddr_to_string(peer, dbg_str, sizeof(dbg_str));
175	if(lan_addr) {
176		syslog(LOG_DEBUG, "%s: %s found in LAN %s %s",
177		       "get_lan_for_peer()", dbg_str,
178		       lan_addr->ifname, lan_addr->str);
179	} else {
180		syslog(LOG_DEBUG, "%s: %s not found !", "get_lan_for_peer()",
181		       dbg_str);
182	}
183#endif /* DEBUG */
184	return lan_addr;
185}
186
187