1 /* $Id: upnpc-libevent.c,v 1.11 2014/12/02 13:33:42 nanard Exp $ */
2 /* miniupnpc-libevent
3  * Copyright (c) 2008-2014, Thomas BERNARD <miniupnp@free.fr>
4  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
17 
18 #include <stdio.h>
19 #include <string.h>
20 #include <signal.h>
21 #include <unistd.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 
26 #include "miniupnpc-libevent.h"
27 
28 static struct event_base *base = NULL;
29 static char local_address[32];
30 
sighandler(int signal)31 static void sighandler(int signal)
32 {
33 	(void)signal;
34 	/*printf("signal %d\n", signal);*/
35 	if(base != NULL)
36 		event_base_loopbreak(base);
37 }
38 
39 /* ready callback */
ready(int code,upnpc_t * p,upnpc_device_t * d,void * data)40 static void ready(int code, upnpc_t * p, upnpc_device_t * d, void * data)
41 {
42 	(void)data; (void)p;
43 
44 	if(code == 200) {
45 		printf("READY ! %d\n", code);
46 		printf("  root_desc_location='%s'\n", d->root_desc_location);
47 		/* 1st request */
48 #ifdef ENABLE_UPNP_EVENTS
49 		upnpc_event_subscribe(d);
50 #else
51 		upnpc_get_status_info(d);
52 #endif /* ENABLE_UPNP_EVENTS */
53 	} else {
54 		printf("DISCOVER ERROR : %d\n", code);
55 		switch(code) {
56 		case UPNPC_ERR_NO_DEVICE_FOUND:
57 			printf("UPNPC_ERR_NO_DEVICE_FOUND\n");
58 			break;
59 		case UPNPC_ERR_ROOT_DESC_ERROR:
60 			printf("UPNPC_ERR_ROOT_DESC_ERROR\n");
61 			break;
62 		case 404:
63 			printf("Root desc not found (404)\n");
64 			break;
65 		default:
66 			printf("unknown error\n");
67 		}
68 	}
69 }
70 
71 static enum {
72 	EGetStatusInfo = 0,
73 	EGetExtIp,
74 	EGetMaxRate,
75 	EAddPortMapping,
76 	EDeletePortMapping,
77 	EFinished
78 	} state = EGetStatusInfo;
79 
80 /* soap callback */
soap(int code,upnpc_t * p,upnpc_device_t * d,void * data)81 static void soap(int code, upnpc_t * p, upnpc_device_t * d, void * data)
82 {
83 	(void)data; (void)p;
84 
85 	printf("SOAP ! %d\n", code);
86 	if(code == 200) {
87 		switch(state) {
88 		case EGetStatusInfo:
89 			printf("ConnectionStatus=%s\n", GetValueFromNameValueList(&d->soap_response_data, "NewConnectionStatus"));
90 			printf("LastConnectionError=%s\n", GetValueFromNameValueList(&d->soap_response_data, "NewLastConnectionError"));
91 			printf("Uptime=%s\n", GetValueFromNameValueList(&d->soap_response_data, "NewUptime"));
92 			upnpc_get_external_ip_address(d);
93 			state = EGetExtIp;
94 			break;
95 		case EGetExtIp:
96 			printf("ExternalIpAddress=%s\n", GetValueFromNameValueList(&d->soap_response_data, "NewExternalIPAddress"));
97 			upnpc_get_link_layer_max_rate(d);
98 			state = EGetMaxRate;
99 			break;
100 		case EGetMaxRate:
101 			printf("DownStream MaxBitRate = %s\t", GetValueFromNameValueList(&d->soap_response_data, "NewLayer1DownstreamMaxBitRate"));
102 			upnpc_add_port_mapping(d, NULL, 60001, 60002, local_address, "TCP", "test port mapping", 0);
103 			printf("UpStream MaxBitRate = %s\n", GetValueFromNameValueList(&d->soap_response_data, "NewLayer1UpstreamMaxBitRate"));
104 			state = EAddPortMapping;
105 			break;
106 		case EAddPortMapping:
107 			printf("AddPortMapping OK!\n");
108 			upnpc_delete_port_mapping(d, NULL, 60001, "TCP");
109 			state = EDeletePortMapping;
110 			break;
111 		case EDeletePortMapping:
112 			printf("DeletePortMapping OK!\n");
113 			state = EFinished;
114 			break;
115 		default:
116 			printf("EFinished : breaking\n");
117 			event_base_loopbreak(base);
118 		}
119 	} else {
120 		printf("SOAP error :\n");
121 		printf("  faultcode='%s'\n", GetValueFromNameValueList(&d->soap_response_data, "faultcode"));
122 		printf("  faultstring='%s'\n", GetValueFromNameValueList(&d->soap_response_data, "faultstring"));
123 		printf("  errorCode=%s\n", GetValueFromNameValueList(&d->soap_response_data, "errorCode"));
124 		printf("  errorDescription='%s'\n", GetValueFromNameValueList(&d->soap_response_data, "errorDescription"));
125 		event_base_loopbreak(base);
126 	}
127 }
128 
129 #ifdef ENABLE_UPNP_EVENTS
130 /* event callback */
event_callback(upnpc_t * p,upnpc_device_t * d,void * data,const char * service_id,const char * property_name,const char * property_value)131 static void event_callback(upnpc_t * p, upnpc_device_t * d, void * data,
132                            const char * service_id, const char * property_name, const char * property_value)
133 {
134 	(void)p; (void)d; (void)data;
135 	printf("PROPERTY VALUE CHANGE (service=%s): %s=%s\n", service_id, property_name, property_value);
136 }
137 #endif /* ENABLE_UPNP_EVENTS */
138 
139 /* use a UDP "connection" to 8.8.8.8
140  * to retrieve local address */
find_local_address(void)141 int find_local_address(void)
142 {
143 	int s;
144 	struct sockaddr_in local, remote;
145 	socklen_t len;
146 
147 	s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
148 	if(s < 0) {
149 		perror("socket");
150 		return -1;
151 	}
152 
153 	memset(&local, 0, sizeof(local));
154 	memset(&remote, 0, sizeof(remote));
155 	/* bind to local port 4567 */
156 	local.sin_family = AF_INET;
157 	local.sin_port = htons(4567);
158 	local.sin_addr.s_addr = htonl(INADDR_ANY);
159 	if(bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) {
160 		perror("bind");
161 		return -1;
162 	}
163 	/* "connect" google's DNS server at 8.8.8.8 port 4567 */
164 	remote.sin_family = AF_INET;
165 	remote.sin_port = htons(4567);
166 	remote.sin_addr.s_addr = inet_addr("8.8.8.8");
167 	if(connect(s, (struct sockaddr *)&remote, sizeof(remote)) < 0) {
168 		perror("connect");
169 		return -1;
170 	}
171 	len = sizeof(local);
172 	if(getsockname(s, (struct sockaddr *)&local, &len) < 0) {
173 		perror("getsockname");
174 		return -1;
175 	}
176 	if(inet_ntop(AF_INET, &(local.sin_addr), local_address, sizeof(local_address)) == NULL) {
177 		perror("inet_ntop");
178 		return -1;
179 	}
180 	printf("local address : %s\n", local_address);
181 	close(s);
182 	return 0;
183 }
184 
185 /* program entry point */
186 
main(int argc,char ** argv)187 int main(int argc, char * * argv)
188 {
189 	struct sigaction sa;
190 	upnpc_t upnp;
191 	char * multicast_if = NULL;
192 
193 	if(argc > 1) {
194 		multicast_if = argv[1];
195 	}
196 
197 	memset(&sa, 0, sizeof(struct sigaction));
198 	sa.sa_handler = sighandler;
199 	if(sigaction(SIGINT, &sa, NULL) < 0) {
200 		perror("sigaction");
201 	}
202 
203 	if(find_local_address() < 0) {
204 		fprintf(stderr, "failed to get local address\n");
205 		return 1;
206 	}
207 #ifdef DEBUG
208 	event_enable_debug_mode();
209 #if LIBEVENT_VERSION_NUMBER >= 0x02010100
210 	event_enable_debug_logging(EVENT_DBG_ALL);	/* Libevent 2.1.1 */
211 #endif /* LIBEVENT_VERSION_NUMBER >= 0x02010100 */
212 #endif /* DEBUG */
213 	printf("Using libevent %s\n", event_get_version());
214 	if(LIBEVENT_VERSION_NUMBER != event_get_version_number()) {
215 		fprintf(stderr, "WARNING build using libevent %s", LIBEVENT_VERSION);
216 	}
217 
218 	base = event_base_new();
219 	if(base == NULL) {
220 		fprintf(stderr, "event_base_new() failed\n");
221 		return 1;
222 	}
223 #ifdef DEBUG
224 	printf("Using Libevent with backend method %s.\n",
225         event_base_get_method(base));
226 #endif /* DEBUG */
227 
228 	if(upnpc_init(&upnp, base, multicast_if, ready, soap, &upnp) != UPNPC_OK) {
229 		fprintf(stderr, "upnpc_init() failed\n");
230 		return 1;
231 	}
232 	upnpc_set_local_address(&upnp, local_address, 50000);
233 #ifdef ENABLE_UPNP_EVENTS
234 	upnpc_set_event_callback(&upnp, event_callback);
235 #endif /* ENABLE_UPNP_EVENTS */
236 	if(upnpc_start(&upnp) != UPNPC_OK) {
237 		fprintf(stderr, "upnp_start() failed\n");
238 		return 1;
239 	}
240 
241 	event_base_dispatch(base);	/* TODO : check return value */
242 	printf("finishing...\n");
243 
244 	upnpc_finalize(&upnp);
245 	event_base_free(base);
246 
247 #if LIBEVENT_VERSION_NUMBER >= 0x02010100
248 	libevent_global_shutdown();	/* Libevent 2.1.1 */
249 #endif
250 	return 0;
251 }
252 
253