/* $Id: upnpc-libevent.c,v 1.8 2014/11/18 09:10:16 nanard Exp $ */ /* miniupnpc-libevent * Copyright (c) 2008-2014, Thomas BERNARD * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "miniupnpc-libevent.h" static struct event_base *base = NULL; static char local_address[32]; static void sighandler(int signal) { (void)signal; /*printf("signal %d\n", signal);*/ if(base != NULL) event_base_loopbreak(base); } /* ready callback */ static void ready(int code, void * data) { upnpc_t * p = (upnpc_t *)data; printf("READY ! %d %p\n", code, data); /* 1st request */ upnpc_get_status_info(p); } static enum { EGetStatusInfo = 0, EGetExtIp, EGetMaxRate, EAddPortMapping, EDeletePortMapping, EFinished } state = EGetStatusInfo; /* soap callback */ static void soap(int code, void * data) { upnpc_t * p = (upnpc_t *)data; printf("SOAP ! %d\n", code); if(code == 200) { switch(state) { case EGetStatusInfo: printf("ConnectionStatus=%s\n", GetValueFromNameValueList(&p->soap_response_data, "NewConnectionStatus")); printf("LastConnectionError=%s\n", GetValueFromNameValueList(&p->soap_response_data, "NewLastConnectionError")); printf("Uptime=%s\n", GetValueFromNameValueList(&p->soap_response_data, "NewUptime")); upnpc_get_external_ip_address(p); state = EGetExtIp; break; case EGetExtIp: printf("ExternalIpAddress=%s\n", GetValueFromNameValueList(&p->soap_response_data, "NewExternalIPAddress")); upnpc_get_link_layer_max_rate(p); state = EGetMaxRate; break; case EGetMaxRate: printf("DownStream MaxBitRate = %s\t", GetValueFromNameValueList(&p->soap_response_data, "NewLayer1DownstreamMaxBitRate")); upnpc_add_port_mapping(p, NULL, 60001, 60002, local_address, "TCP", "test port mapping", 0); printf("UpStream MaxBitRate = %s\n", GetValueFromNameValueList(&p->soap_response_data, "NewLayer1UpstreamMaxBitRate")); state = EAddPortMapping; break; case EAddPortMapping: printf("OK!\n"); upnpc_delete_port_mapping(p, NULL, 60001, "TCP"); state = EDeletePortMapping; break; case EDeletePortMapping: printf("OK!\n"); state = EFinished; default: event_base_loopbreak(base); } } else { printf("SOAP error :\n"); printf(" faultcode='%s'\n", GetValueFromNameValueList(&p->soap_response_data, "faultcode")); printf(" faultstring='%s'\n", GetValueFromNameValueList(&p->soap_response_data, "faultstring")); printf(" errorCode=%s\n", GetValueFromNameValueList(&p->soap_response_data, "errorCode")); printf(" errorDescription='%s'\n", GetValueFromNameValueList(&p->soap_response_data, "errorDescription")); event_base_loopbreak(base); } } /* use a UDP "connection" to 8.8.8.8 * to retrieve local address */ int find_local_address(void) { int s; struct sockaddr_in local, remote; socklen_t len; s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(s < 0) { perror("socket"); return -1; } memset(&local, 0, sizeof(local)); memset(&remote, 0, sizeof(remote)); /* bind to local port 4567 */ local.sin_family = AF_INET; local.sin_port = htons(4567); local.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(s, (struct sockaddr *)&local, sizeof(local)) < 0) { perror("bind"); return -1; } /* "connect" google's DNS server at 8.8.8.8 port 4567 */ remote.sin_family = AF_INET; remote.sin_port = htons(4567); remote.sin_addr.s_addr = inet_addr("8.8.8.8"); if(connect(s, (struct sockaddr *)&remote, sizeof(remote)) < 0) { perror("connect"); return -1; } len = sizeof(local); if(getsockname(s, (struct sockaddr *)&local, &len) < 0) { perror("getsockname"); return -1; } if(inet_ntop(AF_INET, &(local.sin_addr), local_address, sizeof(local_address)) == NULL) { perror("inet_ntop"); return -1; } printf("local address : %s\n", local_address); close(s); return 0; } /* program entry point */ int main(int argc, char * * argv) { struct sigaction sa; upnpc_t upnp; char * multicast_if = NULL; if(argc > 1) { multicast_if = argv[1]; } memset(&sa, 0, sizeof(struct sigaction)); sa.sa_handler = sighandler; if(sigaction(SIGINT, &sa, NULL) < 0) { perror("sigaction"); } if(find_local_address() < 0) { fprintf(stderr, "failed to get local address\n"); return 1; } #ifdef DEBUG event_enable_debug_mode(); #if LIBEVENT_VERSION_NUMBER >= 0x02010100 event_enable_debug_logging(EVENT_DBG_ALL); /* Libevent 2.1.1 */ #endif /* LIBEVENT_VERSION_NUMBER >= 0x02010100 */ #endif /* DEBUG */ printf("Using libevent %s\n", event_get_version()); if(LIBEVENT_VERSION_NUMBER != event_get_version_number()) { fprintf(stderr, "WARNING build using libevent %s", LIBEVENT_VERSION); } base = event_base_new(); if(base == NULL) { fprintf(stderr, "event_base_new() failed\n"); return 1; } #ifdef DEBUG printf("Using Libevent with backend method %s.\n", event_base_get_method(base)); #endif /* DEBUG */ if(upnpc_init(&upnp, base, multicast_if, ready, soap, &upnp) != UPNPC_OK) { fprintf(stderr, "upnpc_init() failed\n"); return 1; } event_base_dispatch(base); /* TODO : check return value */ printf("finishing...\n"); upnpc_finalize(&upnp); event_base_free(base); #if LIBEVENT_VERSION_NUMBER >= 0x02010100 libevent_global_shutdown(); /* Libevent 2.1.1 */ #endif return 0; }