Changeset 3731
- Timestamp:
- Nov 6, 2007, 4:02:50 PM (15 years ago)
- Location:
- trunk
- Files:
-
- 24 added
- 4 deleted
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/cli/Makefile.am
r3621 r3731 11 11 $(top_builddir)/libtransmission/libtransmission.a \ 12 12 $(top_builddir)/third-party/libevent/libevent.la \ 13 $(top_builddir)/third-party/miniupnp/libminiupnp.a \ 13 14 $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm 14 15 -
trunk/configure.ac
r3729 r3731 208 208 libtransmission/Makefile 209 209 third-party/Makefile 210 third-party/miniupnp/Makefile 210 211 macosx/Makefile 211 212 wx/Makefile -
trunk/daemon/Makefile.am
r3621 r3731 29 29 ./libdaemon.a \ 30 30 $(top_builddir)/libtransmission/libtransmission.a \ 31 $(top_builddir)/third-party/miniupnp/libminiupnp.a \ 31 32 $(top_builddir)/third-party/libevent/libevent.la \ 32 33 $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm -
trunk/gtk/Makefile.am
r3621 r3731 56 56 $(top_builddir)/libtransmission/libtransmission.a \ 57 57 $(top_builddir)/third-party/libevent/libevent.la \ 58 $(top_builddir)/third-party/miniupnp/libminiupnp.a \ 58 59 $(GTK_LIBS) \ 59 60 $(OPENSSL_LIBS) \ -
trunk/libtransmission/Makefile.am
r3652 r3731 1 AM_CPPFLAGS = -I. -I$(top_srcdir) - D__TRANSMISSION__ $(LIBEVENT_CPPFLAGS)1 AM_CPPFLAGS = -I. -I$(top_srcdir) -I$(top_srcdir)/third-party/ -D__TRANSMISSION__ $(LIBEVENT_CPPFLAGS) 2 2 AM_CFLAGS = $(OPENSSL_CFLAGS) $(PTHREAD_CFLAGS) 3 3 … … 12 12 fdlimit.c \ 13 13 handshake.c \ 14 http.c \15 14 inout.c \ 16 15 ipcparse.c \ … … 35 34 trevent.c \ 36 35 upnp.c \ 37 utils.c \ 38 xml.c 36 utils.c 39 37 40 38 noinst_HEADERS = \ … … 47 45 fdlimit.h \ 48 46 handshake.h \ 49 http.h \50 47 inout.h \ 51 48 internal.h \ … … 70 67 trevent.h \ 71 68 upnp.h \ 72 utils.h \ 73 xml.h 69 utils.h 74 70 75 71 EXTRA_libtransmission_a_SOURCES = \ -
trunk/libtransmission/metainfo.c
r3473 r3731 32 32 #include <unistd.h> /* unlink, stat */ 33 33 34 #include <miniupnp/miniwget.h> /* parseURL */ 35 34 36 #include "transmission.h" 35 37 #include "bencode.h" 36 38 #include "crypto.h" /* tr_sha1 */ 37 #include "http.h" /* tr_httpParseUrl */38 39 #include "metainfo.h" 39 40 #include "platform.h" 40 41 #include "utils.h" 42 43 44 static int 45 tr_httpParseUrl( const char * url_in, int len, 46 char ** setme_host, int * setme_port, char ** setme_path ) 47 { 48 char * url = tr_strndup( url_in, len ); 49 char * path; 50 char host[4096+1]; 51 unsigned short port; 52 int success; 53 54 success = parseURL( url, host, &port, &path ); 55 56 if( success ) { 57 *setme_host = tr_strdup( host ); 58 *setme_port = port; 59 *setme_path = tr_strdup( path ); 60 } 61 62 tr_free( url ); 63 64 return !success; 65 } 41 66 42 67 /*********************************************************************** -
trunk/libtransmission/natpmp.c
r3707 r3731 84 84 } tr_natpmp_req_t; 85 85 86 struct tr_natpmp _s86 struct tr_natpmp 87 87 { 88 88 #define PMP_STATE_IDLE 1 … … 115 115 116 116 static void 117 unmap( tr_natpmp _t* pmp );117 unmap( tr_natpmp * pmp ); 118 118 static int 119 119 checktime( tr_natpmp_uptime_t * uptime, uint32_t seen ); … … 127 127 resetreq( tr_natpmp_req_t * req ); 128 128 static tr_tristate_t 129 pulsereq( tr_natpmp _t* req );129 pulsereq( tr_natpmp * req ); 130 130 static int 131 131 sendreq( tr_natpmp_req_t * req ); … … 133 133 mcastsetup(); 134 134 static void 135 mcastpulse( tr_natpmp _t* pmp );135 mcastpulse( tr_natpmp * pmp ); 136 136 static tr_tristate_t 137 137 parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse ); 138 138 139 tr_natpmp _t*139 tr_natpmp * 140 140 tr_natpmpInit() 141 141 { 142 tr_natpmp _t* pmp;142 tr_natpmp * pmp; 143 143 144 144 pmp = calloc( 1, sizeof( *pmp ) ); … … 171 171 172 172 void 173 tr_natpmpStart( tr_natpmp _t* pmp )173 tr_natpmpStart( tr_natpmp * pmp ) 174 174 { 175 175 if( !pmp->active ) … … 185 185 186 186 void 187 tr_natpmpStop( tr_natpmp _t* pmp )187 tr_natpmpStop( tr_natpmp * pmp ) 188 188 { 189 189 if( pmp->active ) … … 197 197 198 198 int 199 tr_natpmpStatus( tr_natpmp _t* pmp )199 tr_natpmpStatus( tr_natpmp * pmp ) 200 200 { 201 201 int ret; … … 240 240 241 241 void 242 tr_natpmpForwardPort( tr_natpmp _t* pmp, int port )242 tr_natpmpForwardPort( tr_natpmp * pmp, int port ) 243 243 { 244 244 tr_inf( "nat-pmp set port %i", port ); … … 247 247 248 248 void 249 tr_natpmpRemoveForwarding( tr_natpmp _t* pmp )249 tr_natpmpRemoveForwarding( tr_natpmp * pmp ) 250 250 { 251 251 tr_inf( "nat-pmp unset port" ); … … 255 255 256 256 void 257 tr_natpmpClose( tr_natpmp _t* pmp )257 tr_natpmpClose( tr_natpmp * pmp ) 258 258 { 259 259 /* try to send at least one delete request if we have a port mapping */ … … 266 266 267 267 void 268 tr_natpmpPulse( tr_natpmp _t* pmp, int * publicPort )268 tr_natpmpPulse( tr_natpmp * pmp, int * publicPort ) 269 269 { 270 270 if( 0 <= pmp->mcastfd ) … … 444 444 445 445 void 446 unmap( tr_natpmp _t* pmp )446 unmap( tr_natpmp * pmp ) 447 447 { 448 448 switch( pmp->state ) … … 568 568 569 569 static tr_tristate_t 570 pulsereq( tr_natpmp _t* pmp )570 pulsereq( tr_natpmp * pmp ) 571 571 { 572 572 tr_natpmp_req_t * req = pmp->req; … … 693 693 694 694 static void 695 mcastpulse( tr_natpmp _t* pmp )695 mcastpulse( tr_natpmp * pmp ) 696 696 { 697 697 struct sockaddr_in sin; -
trunk/libtransmission/natpmp.h
r1720 r3731 26 26 #define TR_NATPMP_H 1 27 27 28 typedef struct tr_natpmp _s tr_natpmp_t;28 typedef struct tr_natpmp tr_natpmp; 29 29 30 tr_natpmp _t* tr_natpmpInit();31 void tr_natpmpStart( tr_natpmp _t* );32 void tr_natpmpStop( tr_natpmp _t* );33 int tr_natpmpStatus( tr_natpmp _t* );34 void tr_natpmpForwardPort( tr_natpmp _t*, int );35 void tr_natpmpRemoveForwarding( tr_natpmp _t* );36 void tr_natpmpPulse( tr_natpmp _t*, int * );37 void tr_natpmpClose( tr_natpmp _t* );30 tr_natpmp * tr_natpmpInit(); 31 void tr_natpmpStart( tr_natpmp * ); 32 void tr_natpmpStop( tr_natpmp * ); 33 int tr_natpmpStatus( tr_natpmp * ); 34 void tr_natpmpForwardPort( tr_natpmp *, int ); 35 void tr_natpmpRemoveForwarding( tr_natpmp * ); 36 void tr_natpmpPulse( tr_natpmp *, int * ); 37 void tr_natpmpClose( tr_natpmp * ); 38 38 39 39 #endif -
trunk/libtransmission/shared.c
r3265 r3731 53 53 54 54 /* NAT-PMP/UPnP */ 55 tr_natpmp _t* natpmp;56 tr_upnp _t* upnp;55 tr_natpmp * natpmp; 56 tr_upnp * upnp; 57 57 }; 58 58 -
trunk/libtransmission/tracker.c
r3580 r3731 625 625 626 626 evbuffer_add_printf( buf, "%s" 627 "% sinfo_hash=%s"627 "%cinfo_hash=%s" 628 628 "&peer_id=%s" 629 629 "&port=%d" … … 640 640 "%s%s", 641 641 ann, 642 ( strchr(ann, '?') == NULL ? "?" : "&" ),642 strchr(ann, '?') ? '&' : '?', 643 643 t->escaped, 644 644 t->peer_id, -
trunk/libtransmission/upnp.c
r3707 r3731 1 /* *****************************************************************************2 * $Id$1 /* 2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com> 3 3 * 4 * Copyright (c) 2006-2007 Transmission authors and contributors 4 * This file is licensed by the GPL version 2. Works owned by the 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 7 * This exemption does not extend to derived works not owned by 8 * the Transmission project. 5 9 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 *****************************************************************************/ 10 * $Id:$ 11 */ 24 12 25 #include <assert.h> 26 #include <errno.h> 27 #include <limits.h> 28 #include <stdarg.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <time.h> 13 #include <stdio.h> /* printf */ 33 14 34 #ifdef __BEOS__ 35 #include <netdb.h> 36 #endif 37 38 #include <sys/types.h> 15 #include <miniupnp/miniwget.h> 16 #include <miniupnp/miniupnpc.h> 17 #include <miniupnp/upnpcommands.h> 39 18 40 19 #include "transmission.h" 41 #include "http.h" 42 #include "net.h" 43 #include "trcompat.h" 20 #include "internal.h" 21 #include "utils.h" 44 22 #include "upnp.h" 45 #include "utils.h"46 #include "xml.h"47 23 48 /* uncomment this to log requests and responses to ~/transmission-upnp.log */ 49 /* #define VERBOSE_LOG */ 50 51 #ifdef VERBOSE_LOG 52 #include "platform.h" 53 #endif 54 55 #define SSDP_ADDR "239.255.255.250" 56 #define SSDP_PORT 1900 57 #define SSDP_TYPE "upnp:rootdevice" 58 #define SSDP_SUBTYPE "ssdp:alive" 59 #define SSDP_FIRST_DELAY 3750 /* 3 3/4 seconds */ 60 #define SSDP_MAX_DELAY 1800000 /* 30 minutes */ 61 #define SERVICE_TYPE_IP "urn:schemas-upnp-org:service:WANIPConnection:1" 62 #define SERVICE_TYPE_PPP "urn:schemas-upnp-org:service:WANPPPConnection:1" 63 #define SOAP_ENVELOPE "http://schemas.xmlsoap.org/soap/envelope/" 64 #define LOOP_DETECT_THRESHOLD 10 /* error on 10 add/get/del state changes */ 65 #define MAPPING_CHECK_INTERVAL 900000 /* 15 minutes */ 66 #define HTTP_REQUEST_INTERVAL 500 /* half a second */ 67 #define SOAP_METHOD_NOT_ALLOWED 405 68 #define IGD_GENERIC_ERROR 500 69 #define IGD_GENERIC_FAILED 501 70 #define IGD_NO_MAPPING_EXISTS 714 71 #define IGD_ADD_CONFLICT 718 72 #define IGD_NO_DYNAMIC_MAPPING 725 73 74 struct upnp_act 24 struct tr_upnp 75 25 { 76 char * name; 77 int len; 78 struct { char * name; char * var; char dir; } * args; 26 struct UPNPUrls urls; 27 struct IGDdatas data; 28 int port; 29 char lanaddr[16]; 30 unsigned int isForwarding : 1; 31 unsigned int isEnabled : 1; 79 32 }; 80 33 81 struct upnp_dev 82 { 83 char * id; 84 char * host; 85 char * root; 86 int port; 87 int ppp; 88 char * soap; 89 char * scpd; 90 int mappedport; 91 char * myaddr; 92 #define UPNPDEV_STATE_ROOT 1 93 #define UPNPDEV_STATE_SCPD 2 94 #define UPNPDEV_STATE_READY 3 95 #define UPNPDEV_STATE_ADD 4 96 #define UPNPDEV_STATE_GET 5 97 #define UPNPDEV_STATE_DEL 6 98 #define UPNPDEV_STATE_MAPPED 7 99 #define UPNPDEV_STATE_ERROR 8 100 uint8_t state; 101 uint8_t looping; 102 uint64_t lastrequest; 103 uint64_t lastcheck; 104 unsigned int soapretry : 1; 105 tr_http_t * http; 106 struct upnp_act getcmd; 107 struct upnp_act addcmd; 108 struct upnp_act delcmd; 109 struct upnp_dev * next; 110 }; 34 /** 35 *** 36 **/ 111 37 112 struct tr_upnp_s 113 { 114 int port; 115 int infd; 116 int outfd; 117 uint64_t lastdiscover; 118 uint64_t lastdelay; 119 unsigned int active : 1; 120 unsigned int discovering : 1; 121 struct upnp_dev * devices; 122 }; 123 124 static int 125 sendSSDP( int fd ); 126 static int 127 mcastStart( void ); 128 static void 129 killSock( int * sock ); 130 static void 131 killHttp( tr_http_t ** http ); 132 static int 133 watchSSDP( struct upnp_dev ** devices, int fd ); 134 static tr_tristate_t 135 recvSSDP( int fd, char * buf, int * len ); 136 static int 137 parseSSDP( char * buf, int len, tr_http_header_t * headers ); 138 static void 139 deviceAdd( struct upnp_dev ** first, const char * id, int idLen, 140 const char * url, int urlLen ); 141 static void 142 deviceRemove( struct upnp_dev ** prevptr ); 143 static int 144 deviceStop( struct upnp_dev * dev ); 145 static int 146 devicePulse( struct upnp_dev * dev, int port ); 147 static int 148 devicePulseHttp( struct upnp_dev * dev, 149 const char ** body, int * len ); 150 static tr_http_t * 151 devicePulseGetHttp( struct upnp_dev * dev ); 152 static int 153 parseRoot( const char * root, const char *buf, int len, 154 char ** soap, char ** scpd, int * ppp ); 155 static void 156 addUrlbase( const char * base, char ** path ); 157 static int 158 parseScpd( const char *buf, int len, struct upnp_act * getcmd, 159 struct upnp_act * addcmd, struct upnp_act * delcmd ); 160 static int 161 parseScpdArgs( const char * buf, const char * end, 162 struct upnp_act * action, char dir ); 163 static int 164 parseMapping( struct upnp_dev * dev, const char * buf, int len ); 165 static char * 166 joinstrs( const char *, const char *, const char * ); 167 static tr_http_t * 168 soapRequest( int retry, const char * host, int port, const char * path, 169 const char * type, struct upnp_act * action, ... ); 170 static void 171 actionSetup( struct upnp_act * action, const char * name, int prealloc ); 172 static void 173 actionFree( struct upnp_act * action ); 174 static int 175 actionAdd( struct upnp_act * action, char * name, char * var, 176 char dir ); 177 #define actionLookupVar( act, nam, len, dir ) \ 178 ( actionLookup( (act), (nam), (len), (dir), 0 ) ) 179 #define actionLookupName( act, var, len, dir ) \ 180 ( actionLookup( (act), (var), (len), (dir), 1 ) ) 181 static const char * 182 actionLookup( struct upnp_act * action, const char * key, int len, 183 char dir, int getname ); 184 185 #ifdef VERBOSE_LOG 186 static FILE * vlog = NULL; 187 #endif 188 189 tr_upnp_t * 38 tr_upnp* 190 39 tr_upnpInit( void ) 191 40 { 192 tr_upnp_t * upnp; 193 194 upnp = calloc( 1, sizeof( *upnp ) ); 195 if( NULL == upnp ) 196 { 197 return NULL; 41 tr_upnp * ret = tr_new0( tr_upnp, 1 ); 42 struct UPNPDev * devlist = upnpDiscover( 2000, NULL ); 43 if( UPNP_GetValidIGD( devlist, &ret->urls, &ret->data, ret->lanaddr, sizeof(ret->lanaddr))) { 44 tr_dbg( "Found Internet Gateway Device '%s'", ret->urls.controlURL ); 45 tr_dbg( "Local LAN IP Address is '%s'", ret->lanaddr ); 198 46 } 199 200 upnp->infd = -1; 201 upnp->outfd = -1; 202 203 #ifdef VERBOSE_LOG 204 if( NULL == vlog ) 205 { 206 char path[MAX_PATH_LENGTH]; 207 time_t stupid_api; 208 snprintf( path, sizeof path, "%s/transmission-upnp.log", 209 tr_getHomeDirectory()); 210 vlog = fopen( path, "a" ); 211 stupid_api = time( NULL ); 212 fprintf( vlog, "opened log at %s\n\n", ctime( &stupid_api ) ); 213 fflush( vlog ); 214 } 215 #endif 216 217 return upnp; 47 ret->port = -1; 48 freeUPNPDevlist( devlist ); 49 return ret; 218 50 } 219 51 220 52 void 221 tr_upnp Start( tr_upnp_t * upnp)53 tr_upnpClose( tr_upnp * handle ) 222 54 { 223 if( !upnp->active ) 55 tr_upnpStop( handle ); 56 FreeUPNPUrls( &handle->urls ); 57 tr_free( handle ); 58 } 59 60 /** 61 *** 62 **/ 63 64 void 65 tr_upnpStart( tr_upnp * handle ) 66 { 67 handle->isEnabled = 1; 68 69 if( handle->port >= 0 ) 224 70 { 225 tr_inf( "starting upnp" ); 226 upnp->active = 1; 227 upnp->discovering = 1; 228 upnp->infd = mcastStart(); 229 upnp->lastdiscover = 0; 230 upnp->lastdelay = SSDP_FIRST_DELAY / 2; 71 char portStr[16]; 72 snprintf( portStr, sizeof(portStr), "%d", handle->port ); 73 handle->isForwarding = UPNP_AddPortMapping( handle->urls.controlURL, 74 handle->data.servicetype, 75 portStr, portStr, handle->lanaddr, 76 "Transmission", "TCP" ); 77 78 tr_dbg( "UPNP Port Forwarding via '%s', service '%s'. (local address: %s:%d)", 79 handle->urls.controlURL, handle->data.servicetype, handle->lanaddr, handle->port ); 80 tr_dbg( "UPNP Port Forwarding Enabled? %s", (handle->isForwarding?"Yes":"No") ); 231 81 } 232 82 } 233 83 234 84 void 235 tr_upnp Stop( tr_upnp_t * upnp)85 tr_upnpRemoveForwarding ( tr_upnp * handle ) 236 86 { 237 if( upnp->active ) 87 handle->port = -1; 88 89 if( handle->isForwarding ) 238 90 { 239 tr_inf( "stopping upnp" ); 240 upnp->active = 0; 241 killSock( &upnp->infd ); 242 killSock( &upnp->outfd ); 91 char portStr[16]; 92 snprintf( portStr, sizeof(portStr), "%d", handle->port ); 93 94 UPNP_DeletePortMapping( handle->urls.controlURL, 95 handle->data.servicetype, 96 portStr, "TCP" ); 97 tr_dbg( "Stopping port forwarding of '%s', service '%s'", 98 handle->urls.controlURL, handle->data.servicetype ); 99 100 handle->isForwarding = FALSE; 243 101 } 244 102 } 245 103 246 int 247 tr_upnp Status( tr_upnp_t * upnp)104 void 105 tr_upnpForwardPort( tr_upnp * handle, int publicPort ) 248 106 { 249 struct upnp_dev * ii; 250 int ret; 107 tr_upnpRemoveForwarding( handle ); /* remove the old forwarding */ 251 108 252 if( !upnp->active ) 253 { 254 ret = ( NULL == upnp->devices ? 255 TR_NAT_TRAVERSAL_DISABLED : TR_NAT_TRAVERSAL_UNMAPPING ); 256 } 257 else if( NULL == upnp->devices ) 258 { 259 ret = TR_NAT_TRAVERSAL_NOTFOUND; 260 } 261 else 262 { 263 ret = TR_NAT_TRAVERSAL_MAPPING; 264 for( ii = upnp->devices; NULL != ii; ii = ii->next ) 265 { 266 if( UPNPDEV_STATE_ERROR == ii->state ) 267 { 268 ret = TR_NAT_TRAVERSAL_ERROR; 269 } 270 else if( 0 < ii->mappedport ) 271 { 272 ret = TR_NAT_TRAVERSAL_MAPPED; 273 break; 274 } 275 } 276 } 109 handle->port = publicPort; 277 110 278 return ret; 111 if( handle->isEnabled ) 112 tr_upnpStart( handle ); 279 113 } 280 114 281 115 void 282 tr_upnp ForwardPort( tr_upnp_t * upnp, int port)116 tr_upnpStop( tr_upnp * handle ) 283 117 { 284 tr_dbg( "upnp port changed from %i to %i", upnp->port, port ); 285 upnp->port = port; 118 tr_upnpRemoveForwarding( handle ); 119 handle->isEnabled = 0; 120 } 121 122 int 123 tr_upnpStatus( tr_upnp * handle ) 124 { 125 if( !handle->isEnabled ) 126 return TR_NAT_TRAVERSAL_DISABLED; 127 128 if( !handle->isForwarding ) 129 return TR_NAT_TRAVERSAL_ERROR; 130 131 return TR_NAT_TRAVERSAL_MAPPED; 286 132 } 287 133 288 134 void 289 tr_upnp RemoveForwarding( tr_upnp_t * upnp)135 tr_upnpPulse( tr_upnp * handle UNUSED ) 290 136 { 291 tr_dbg( "upnp port unset" ); 292 upnp->port = 0; 137 /* no-op */ 293 138 } 294 295 void296 tr_upnpClose( tr_upnp_t * upnp )297 {298 tr_upnpStop( upnp );299 300 while( NULL != upnp->devices )301 {302 deviceRemove( &upnp->devices );303 }304 305 free( upnp );306 307 #ifdef VERBOSE_LOG308 if( NULL != vlog )309 {310 fclose( vlog );311 }312 #endif313 }314 315 void316 tr_upnpPulse( tr_upnp_t * upnp )317 {318 struct upnp_dev ** ii;319 320 if( upnp->active )321 {322 /* pulse on all known devices */323 upnp->discovering = 1;324 for( ii = &upnp->devices; NULL != *ii; ii = &(*ii)->next )325 {326 if( devicePulse( *ii, upnp->port ) )327 {328 upnp->discovering = 0;329 }330 }331 332 /* send an SSDP discover message */333 if( upnp->discovering &&334 upnp->lastdelay + upnp->lastdiscover < tr_date() )335 {336 upnp->outfd = sendSSDP( upnp->outfd );337 upnp->lastdiscover = tr_date();338 upnp->lastdelay = MIN( upnp->lastdelay * 2, SSDP_MAX_DELAY );339 }340 341 /* try to receive SSDP messages */342 watchSSDP( &upnp->devices, upnp->infd );343 if( watchSSDP( &upnp->devices, upnp->outfd ) )344 {345 killSock( &upnp->outfd );346 }347 }348 else349 {350 /* delete all mappings then delete devices */351 ii = &upnp->devices;352 while( NULL != *ii )353 {354 if( deviceStop( *ii ) )355 {356 deviceRemove( ii );357 }358 else359 {360 devicePulse( *ii, 0 );361 ii = &(*ii)->next;362 }363 }364 }365 }366 367 static int368 sendSSDP( int fd )369 {370 char buf[102];371 int len;372 struct sockaddr_in sin;373 374 if( 0 > fd )375 {376 fd = tr_netBindUDP( 0 );377 if( 0 > fd )378 {379 return -1;380 }381 }382 383 tr_dbg( "sending upnp ssdp discover message" );384 385 len = snprintf( buf, sizeof( buf ),386 "M-SEARCH * HTTP/1.1\r\n"387 "Host: %s:%i\r\n"388 "Man: \"ssdp:discover\"\r\n"389 "ST: %s\r\n"390 "MX: 3\r\n"391 "\r\n",392 SSDP_ADDR, SSDP_PORT, SSDP_TYPE );393 394 /* if this assertion ever fails then just increase the size of buf */395 assert( (int) sizeof( buf ) > len );396 397 memset( &sin, 0, sizeof( sin ) );398 sin.sin_family = AF_INET;399 sin.sin_addr.s_addr = inet_addr( SSDP_ADDR );400 sin.sin_port = htons( SSDP_PORT );401 402 #ifdef VERBOSE_LOG403 fprintf( vlog, "send ssdp message, %i bytes:\n", len );404 fwrite( buf, 1, len, vlog );405 fputs( "\n\n", vlog );406 fflush( vlog );407 #endif408 409 if( 0 > sendto( fd, buf, len, 0,410 (struct sockaddr*) &sin, sizeof( sin ) ) )411 {412 if( EAGAIN != sockerrno )413 {414 tr_err( "Could not send SSDP discover message (%s)",415 strerror( sockerrno ) );416 }417 killSock( &fd );418 return -1;419 }420 421 return fd;422 }423 424 static int425 mcastStart()426 {427 int fd;428 struct in_addr addr;429 430 addr.s_addr = inet_addr( SSDP_ADDR );431 fd = tr_netMcastOpen( SSDP_PORT, &addr );432 if( 0 > fd )433 {434 return -1;435 }436 437 return fd;438 }439 440 static void441 killSock( int * sock )442 {443 if( 0 <= *sock )444 {445 tr_netClose( *sock );446 *sock = -1;447 }448 }449 450 static void451 killHttp( tr_http_t ** http )452 {453 tr_httpClose( *http );454 *http = NULL;455 }456 457 static int458 watchSSDP( struct upnp_dev ** devices, int fd )459 {460 /* XXX what if we get a huge SSDP packet? */461 char buf[512];462 int len;463 tr_http_header_t hdr[] = {464 /* first one must be type and second must be subtype */465 { NULL, NULL, 0 },466 { "NTS", NULL, 0 },467 /* XXX should probably look at this468 { "Cache-control", NULL, 0 }, */469 { "Location", NULL, 0 },470 { "USN", NULL, 0 },471 { NULL, NULL, 0 }472 };473 enum { OFF_TYPE = 0, OFF_SUBTYPE, OFF_LOC, OFF_ID };474 int ret;475 476 if( 0 > fd )477 {478 return 0;479 }480 481 ret = 0;482 for(;;)483 {484 len = sizeof( buf );485 switch( recvSSDP( fd, buf, &len ) )486 {487 case TR_NET_WAIT:488 return ret;489 case TR_NET_ERROR:490 return 1;491 case TR_NET_OK:492 ret = 1;493 if( parseSSDP( buf, len, hdr ) &&494 NULL != hdr[OFF_LOC].data &&495 NULL != hdr[OFF_ID].data )496 {497 deviceAdd( devices, hdr[OFF_ID].data, hdr[OFF_ID].len,498 hdr[OFF_LOC].data, hdr[OFF_LOC].len );499 }500 }501 }502 }503 504 static tr_tristate_t505 recvSSDP( int fd, char * buf, int * len )506 {507 if( 0 > fd )508 {509 return TR_NET_ERROR;510 }511 512 *len = tr_netRecv( fd, ( uint8_t * ) buf, *len );513 if( TR_NET_BLOCK & *len )514 {515 return TR_NET_WAIT;516 }517 else if( TR_NET_CLOSE & *len )518 {519 tr_err( "Could not receive SSDP message (%s)", strerror( sockerrno ) );520 return TR_NET_ERROR;521 }522 else523 {524 #ifdef VERBOSE_LOG525 fprintf( vlog, "receive ssdp message, %i bytes:\n", *len );526 fwrite( buf, 1, *len, vlog );527 fputs( "\n\n", vlog );528 fflush( vlog );529 #endif530 return TR_NET_OK;531 }532 }533 534 static int535 parseSSDP( char * buf, int len, tr_http_header_t * hdr )536 {537 char *method, *uri, *body;538 int code;539 540 body = NULL;541 /* check for an HTTP NOTIFY request */542 if( 0 <= tr_httpRequestType( buf, len, &method, &uri ) )543 {544 if( 0 == tr_strcasecmp( method, "NOTIFY" ) && 0 == strcmp( uri, "*" ) )545 {546 hdr[0].name = "NT";547 body = tr_httpParse( buf, len, hdr );548 if( NULL == hdr[1].name ||549 0 != tr_strncasecmp( SSDP_SUBTYPE, hdr[1].data, hdr[1].len ) )550 {551 body = NULL;552 }553 else554 {555 tr_dbg( "found upnp ssdp notify request" );556 }557 }558 free( method );559 free( uri );560 }561 else562 {563 /* check for a response to our HTTP M-SEARCH request */564 code = tr_httpResponseCode( buf, len );565 if( TR_HTTP_STATUS_OK( code ) )566 {567 hdr[0].name = "ST";568 body = tr_httpParse( buf, len, hdr );569 if( NULL != body )570 {571 tr_dbg( "found upnp ssdp m-search response" );572 }573 }574 }575 576 /* did we find enough information to be useful? */577 if( NULL != body )578 {579 /* the first header is the type */580 if( NULL != hdr[0].data &&581 0 == tr_strncasecmp( SSDP_TYPE, hdr[0].data, hdr[0].len ) )582 {583 return 1;584 }585 }586 587 return 0;588 }589 590 static void591 deviceAdd( struct upnp_dev ** first, const char * id, int idLen,592 const char * url, int urlLen )593 {594 struct upnp_dev * ii;595 596 for( ii = *first; NULL != ii; ii = ii->next )597 {598 if( 0 == tr_strncasecmp( ii->id, id, idLen ) )599 {600 /* this device may have gone away and came back, recheck it */601 ii->lastcheck = 0;602 return;603 }604 }605 606 ii = malloc( sizeof( *ii ) );607 if( NULL == ii )608 {609 return;610 }611 memset( ii, 0, sizeof( *ii ) );612 if( tr_httpParseUrl( url, urlLen, &ii->host, &ii->port, &ii->root ) )613 {614 tr_err( "Invalid HTTP URL from UPnP" );615 free( ii );616 return;617 }618 ii->id = tr_strndup( id, idLen );619 ii->state = UPNPDEV_STATE_ROOT;620 actionSetup( &ii->getcmd, "GetSpecificPortMappingEntry", 8 );621 actionSetup( &ii->addcmd, "AddPortMapping", 8 );622 actionSetup( &ii->delcmd, "DeletePortMapping", 3 );623 ii->next = *first;624 *first = ii;625 626 tr_inf( "new upnp device %s, port %i, path %s",627 ii->host, ii->port, ii->root );628 }629 630 static void631 deviceRemove( struct upnp_dev ** prevptr )632 {633 struct upnp_dev * dead;634 635 dead = *prevptr;636 *prevptr = dead->next;637 638 tr_inf( "forgetting upnp device %s", dead->host );639 640 free( dead->id );641 free( dead->host );642 free( dead->root );643 free( dead->soap );644 free( dead->scpd );645 free( dead->myaddr );646 if( NULL != dead->http )647 {648 killHttp( &dead->http );649 }650 actionFree( &dead->getcmd );651 actionFree( &dead->addcmd );652 actionFree( &dead->delcmd );653 free( dead );654 }655 656 static int657 deviceStop( struct upnp_dev * dev )658 {659 switch( dev->state )660 {661 case UPNPDEV_STATE_READY:662 case UPNPDEV_STATE_ERROR:663 return 1;664 case UPNPDEV_STATE_MAPPED:665 tr_dbg( "upnp device %s: stopping upnp, state mapped -> delete",666 dev->host );667 dev->state = UPNPDEV_STATE_DEL;668 return 0;669 default:670 return 0;671 }672 }673 674 static int675 devicePulse( struct upnp_dev * dev, int port )676 {677 const char * body;678 int len, code;679 uint8_t laststate;680 681 switch( dev->state )682 {683 case UPNPDEV_STATE_READY:684 if( 0 < port )685 {686 tr_dbg( "upnp device %s: want mapping, state ready -> get",687 dev->host );688 dev->mappedport = port;689 dev->state = UPNPDEV_STATE_GET;690 break;691 }692 return 1;693 case UPNPDEV_STATE_MAPPED:694 if( port != dev->mappedport )695 {696 tr_dbg( "upnp device %s: change mapping, "697 "state mapped -> delete", dev->host );698 dev->state = UPNPDEV_STATE_DEL;699 break;700 }701 if( tr_date() > dev->lastcheck + MAPPING_CHECK_INTERVAL )702 {703 tr_dbg( "upnp device %s: check mapping, "704 "state mapped -> get", dev->host );705 dev->state = UPNPDEV_STATE_GET;706 }707 return 1;708 case UPNPDEV_STATE_ERROR:709 return 0;710 }711 712 /* gcc can be pretty annoying about it's warnings sometimes */713 len = 0;714 body = NULL;715 716 code = devicePulseHttp( dev, &body, &len );717 if( 0 > code )718 {719 return 1;720 }721 722 if( LOOP_DETECT_THRESHOLD <= dev->looping )723 {724 tr_dbg( "upnp device %s: loop detected, state %hhu -> error",725 dev->host, dev->state );726 dev->state = UPNPDEV_STATE_ERROR;727 dev->looping = 0;728 killHttp( &dev->http );729 return 1;730 }731 732 laststate = dev->state;733 dev->state = UPNPDEV_STATE_ERROR;734 switch( laststate )735 {736 case UPNPDEV_STATE_ROOT:737 if( !TR_HTTP_STATUS_OK( code ) )738 {739 tr_dbg( "upnp device %s: fetch root failed with http code %i",740 dev->host, code );741 }742 else if( parseRoot( dev->root, body, len,743 &dev->soap, &dev->scpd, &dev->ppp ) )744 {745 tr_dbg( "upnp device %s: parse root failed", dev->host );746 }747 else748 {749 tr_dbg( "upnp device %s: found scpd \"%s\" and soap \"%s\"",750 dev->root, dev->scpd, dev->soap );751 tr_dbg( "upnp device %s: parsed root, state root -> scpd",752 dev->host );753 dev->state = UPNPDEV_STATE_SCPD;754 }755 break;756 757 case UPNPDEV_STATE_SCPD:758 if( !TR_HTTP_STATUS_OK( code ) )759 {760 tr_dbg( "upnp device %s: fetch scpd failed with http code %i",761 dev->host, code );762 }763 else if( parseScpd( body, len, &dev->getcmd,764 &dev->addcmd, &dev->delcmd ) )765 {766 tr_dbg( "upnp device %s: parse scpd failed", dev->host );767 }768 else769 {770 tr_dbg( "upnp device %s: parsed scpd, state scpd -> ready",771 dev->host );772 dev->state = UPNPDEV_STATE_READY;773 dev->looping = 0;774 }775 break;776 777 case UPNPDEV_STATE_ADD:778 dev->looping++;779 if( IGD_ADD_CONFLICT == code )780 {781 tr_dbg( "upnp device %s: add conflict, state add -> delete",782 dev->host );783 dev->state = UPNPDEV_STATE_DEL;784 }785 else if( TR_HTTP_STATUS_OK( code ) ||786 IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code )787 {788 tr_dbg( "upnp device %s: add attempt, state add -> get",789 dev->host );790 dev->state = UPNPDEV_STATE_GET;791 }792 else793 {794 tr_dbg( "upnp device %s: add failed with http code %i",795 dev->host, code );796 }797 break;798 799 case UPNPDEV_STATE_GET:800 dev->looping++;801 if( TR_HTTP_STATUS_OK( code ) )802 {803 switch( parseMapping( dev, body, len ) )804 {805 case -1:806 break;807 case 0:808 tr_dbg( "upnp device %s: invalid mapping, "809 "state get -> delete", dev->host );810 dev->state = UPNPDEV_STATE_DEL;811 break;812 case 1:813 tr_dbg( "upnp device %s: good mapping, "814 "state get -> mapped", dev->host );815 dev->state = UPNPDEV_STATE_MAPPED;816 dev->looping = 0;817 dev->lastcheck = tr_date();818 tr_inf( "upnp successful for port %i",819 dev->mappedport );820 break;821 default:822 assert( 0 );823 break;824 }825 }826 else if( IGD_NO_MAPPING_EXISTS == code ||827 IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code )828 {829 tr_dbg( "upnp device %s: no mapping, state get -> add",830 dev->host );831 dev->state = UPNPDEV_STATE_ADD;832 }833 else834 {835 tr_dbg( "upnp device %s: get failed with http code %i",836 dev->host, code );837 }838 break;839 840 case UPNPDEV_STATE_DEL:841 dev->looping++;842 if( TR_HTTP_STATUS_OK( code ) || IGD_NO_MAPPING_EXISTS == code ||843 IGD_GENERIC_ERROR == code || IGD_GENERIC_FAILED == code )844 {845 tr_dbg( "upnp device %s: deleted, state delete -> ready",846 dev->host );847 dev->state = UPNPDEV_STATE_READY;848 dev->looping = 0;849 }850 else851 {852 tr_dbg( "upnp device %s: del failed with http code %i",853 dev->host, code );854 }855 break;856 857 default:858 assert( 0 );859 break;860 }861 862 dev->lastrequest = tr_date();863 killHttp( &dev->http );864 865 if( UPNPDEV_STATE_ERROR == dev->state )866 {867 tr_dbg( "upnp device %s: error, state %hhu -> error",868 dev->host, laststate );869 return 0;870 }871 872 return 1;873 }874 875 static tr_http_t *876 makeHttp( int method, const char * host, int port, const char * path )877 {878 if( tr_httpIsUrl( path, -1 ) )879 {880 return tr_httpClientUrl( method, "%s", path );881 }882 else883 {884 return tr_httpClient( method, host, port, "%s", path );885 }886 }887 888 static tr_http_t *889 devicePulseGetHttp( struct upnp_dev * dev )890 {891 tr_http_t * ret;892 char numstr[6];893 const char * type;894 #ifdef VERBOSE_LOG895 const char * body;896 int len;897 #endif898 899 ret = NULL;900 switch( dev->state )901 {902 case UPNPDEV_STATE_ROOT:903 if( !dev->soapretry )904 {905 ret = makeHttp( TR_HTTP_GET, dev->host, dev->port, dev->root );906 }907 break;908 case UPNPDEV_STATE_SCPD:909 if( !dev->soapretry )910 {911 ret = makeHttp( TR_HTTP_GET, dev->host, dev->port, dev->scpd );912 }913 break;914 case UPNPDEV_STATE_ADD:915 if( NULL == dev->myaddr )916 {917 ret = NULL;918 break;919 }920 snprintf( numstr, sizeof( numstr ), "%i", dev->mappedport );921 type = ( dev->ppp ? SERVICE_TYPE_PPP : SERVICE_TYPE_IP );922 ret = soapRequest( dev->soapretry, dev->host, dev->port, dev->soap,923 type, &dev->addcmd,924 "PortMappingEnabled", "1",925 "PortMappingLeaseDuration", "0",926 "RemoteHost", "",927 "ExternalPort", numstr,928 "InternalPort", numstr,929 "PortMappingProtocol", "TCP",930 "InternalClient", dev->myaddr,931 "PortMappingDescription", "Added by " TR_NAME,932 NULL );933 break;934 case UPNPDEV_STATE_GET:935 snprintf( numstr, sizeof( numstr ), "%i", dev->mappedport );936 type = ( dev->ppp ? SERVICE_TYPE_PPP : SERVICE_TYPE_IP );937 ret = soapRequest( dev->soapretry, dev->host, dev->port, dev->soap,938 type, &dev->getcmd,939 "RemoteHost", "",940 "ExternalPort", numstr,941 "PortMappingProtocol", "TCP",942 NULL );943 break;944 case UPNPDEV_STATE_DEL:945 snprintf( numstr, sizeof( numstr ), "%i", dev->mappedport );946 type = ( dev->ppp ? SERVICE_TYPE_PPP : SERVICE_TYPE_IP );947 ret = soapRequest( dev->soapretry, dev->host, dev->port, dev->soap,948 type, &dev->delcmd,949 "RemoteHost", "",950 "ExternalPort", numstr,951 "PortMappingProtocol", "TCP",952 NULL );953 break;954 default:955 assert( 0 );956 break;957 }958 959 #ifdef VERBOSE_LOG960 if( NULL != ret )961 {962 tr_httpGetHeaders( ret, &body, &len );963 fprintf( vlog, "send http message, %i bytes (headers):\n", len );964 fwrite( body, 1, len, vlog );965 tr_httpGetBody( ret, &body, &len );966 fprintf( vlog, "\n\nsend http message, %i bytes (body):\n", len );967 fwrite( body, 1, len, vlog );968 fputs( "\n\n", vlog );969 fflush( vlog );970 }971 #endif972 973 return ret;974 }975 976 static int977 devicePulseHttp( struct upnp_dev * dev,978 const char ** body, int * len )979 {980 const char * headers;981 int hlen, code;982 983 if( NULL == dev->http )984 {985 if( tr_date() < dev->lastrequest + HTTP_REQUEST_INTERVAL )986 {987 return -1;988 }989 dev->lastrequest = tr_date();990 dev->http = devicePulseGetHttp( dev );991 if( NULL == dev->http )992 {993 tr_dbg( "upnp device %s: http init failed, state %hhu -> error",994 dev->host, dev->state );995 dev->state = UPNPDEV_STATE_ERROR;996 dev->soapretry = 0;997 return -1;998 }999 }1000 1001 if( NULL == dev->myaddr )1002 {1003 dev->myaddr = tr_httpWhatsMyAddress( dev->http );1004 }1005 1006 switch( tr_httpPulse( dev->http, &headers, &hlen ) )1007 {1008 case TR_NET_OK:1009 #ifdef VERBOSE_LOG1010 fprintf( vlog, "receive http message, %i bytes:\n", hlen );1011 fwrite( headers, 1, hlen, vlog );1012 fputs( "\n\n", vlog );1013 fflush( vlog );1014 #endif1015 code = tr_httpResponseCode( headers, hlen );1016 if( SOAP_METHOD_NOT_ALLOWED == code && !dev->soapretry )1017 {1018 dev->soapretry = 1;1019 killHttp( &dev->http );1020 break;1021 }1022 dev->soapretry = 0;1023 *body = tr_httpParse( headers, hlen, NULL );1024 *len = ( NULL == *body ? 0 : hlen - ( *body - headers ) );1025 return code;1026 case TR_NET_ERROR:1027 killHttp( &dev->http );1028 if( dev->soapretry )1029 {1030 tr_dbg( "upnp device %s: http pulse failed, state %hhu -> error",1031 dev->host, dev->state );1032 dev->state = UPNPDEV_STATE_ERROR;1033 dev->soapretry = 0;1034 }1035 else1036 {1037 dev->soapretry = 1;1038 }1039 break;1040 case TR_NET_WAIT:1041 break;1042 }1043 1044 return -1;1045 }1046 1047 static int1048 parseRoot( const char * root, const char *buf, int len,1049 char ** soap, char ** scpd, int * ppp )1050 {1051 const char * end, * ii, * jj, * kk, * urlbase;1052 char * basedup;1053 1054 *soap = NULL;1055 *scpd = NULL;1056 end = buf + len;1057 1058 buf = tr_xmlFindTagContents( buf, end, "root" );1059 urlbase = tr_xmlFindTag( buf, end, "urlBase" );1060 urlbase = tr_xmlTagContents( urlbase, end );1061 buf = tr_xmlFindTagContents( buf, end, "device" );1062 if( tr_xmlFindTagVerifyContents( buf, end, "deviceType",1063 "urn:schemas-upnp-org:device:InternetGatewayDevice:1", 1 ) )1064 {1065 return 1;1066 }1067 buf = tr_xmlFindTag( buf, end, "deviceList" );1068 ii = tr_xmlTagContents( buf, end );1069 for( ; NULL != ii; ii = tr_xmlSkipTag( ii, end ) )1070 {1071 ii = tr_xmlFindTag( ii, end, "device" );1072 buf = tr_xmlTagContents( ii, end );1073 if( tr_xmlFindTagVerifyContents( buf, end, "deviceType",1074 "urn:schemas-upnp-org:device:WANDevice:1", 1 ) )1075 {1076 continue;1077 }1078 buf = tr_xmlFindTag( buf, end, "deviceList" );1079 jj = tr_xmlTagContents( buf, end );1080 for( ; NULL != jj; jj = tr_xmlSkipTag( jj, end ) )1081 {1082 jj = tr_xmlFindTag( jj, end, "device" );1083 buf = tr_xmlTagContents( jj, end );1084 if( tr_xmlFindTagVerifyContents( buf, end, "deviceType",1085 "urn:schemas-upnp-org:device:WANConnectionDevice:1", 1 ) )1086 {1087 continue;1088 }1089 buf = tr_xmlFindTag( buf, end, "serviceList" );1090 kk = tr_xmlTagContents( buf, end );1091 for( ; NULL != kk; kk = tr_xmlSkipTag( kk, end ) )1092 {1093 kk = tr_xmlFindTag( kk, end, "service" );1094 buf = tr_xmlTagContents( kk, end );1095 if( !tr_xmlFindTagVerifyContents( buf, end, "serviceType",1096 SERVICE_TYPE_IP, 1 ) )1097 {1098 *soap = tr_xmlDupTagContents( buf, end, "controlURL");1099 *scpd = tr_xmlDupTagContents( buf, end, "SCPDURL");1100 *ppp = 0;1101 break;1102 }1103 /* XXX we should save all services of both types and1104 try adding mappings for each in turn */1105 else if( !tr_xmlFindTagVerifyContents( buf, end, "serviceType",1106 SERVICE_TYPE_PPP, 1 ) )1107 {1108 *soap = tr_xmlDupTagContents( buf, end, "controlURL");1109 *scpd = tr_xmlDupTagContents( buf, end, "SCPDURL");1110 *ppp = 1;1111 break;1112 }1113 }1114 }1115 }1116 1117 if( NULL == urlbase )1118 {1119 basedup = strrchr( root, '/' );1120 assert( NULL != basedup );1121 basedup = tr_strndup( root, basedup - root + 1 );1122 }1123 else1124 {1125 basedup = tr_xmlDupContents( urlbase, end );1126 }1127 addUrlbase( basedup, soap );1128 addUrlbase( basedup, scpd );1129 free( basedup );1130 1131 if( NULL != *soap && NULL != *scpd )1132 {1133 return 0;1134 }1135 1136 return 1;1137 }1138 1139 static void1140 addUrlbase( const char * base, char ** path )1141 {1142 const char * middle;1143 int len;1144 char * joined;1145 1146 if( NULL == base || NULL == *path ||1147 '/' == **path || tr_httpIsUrl( *path, -1 ) )1148 {1149 return;1150 }1151 1152 len = strlen( base );1153 middle = ( 0 >= len || '/' != base[len-1] ? "/" : "" );1154 joined = joinstrs( base, middle, *path );1155 free( *path );1156 *path = joined;1157 }1158 1159 static int1160 parseScpd( const char * buf, int len, struct upnp_act * getcmd,1161 struct upnp_act * addcmd, struct upnp_act * delcmd )1162 {1163 const char * end, * next, * sub, * name;1164 1165 end = buf + len;1166 next = buf;1167 1168 next = tr_xmlFindTagContents( next, end, "scpd" );1169 next = tr_xmlFindTagContents( next, end, "actionList" );1170 1171 while( NULL != next )1172 {1173 next = tr_xmlFindTag( next, end, "action" );1174 sub = tr_xmlTagContents( next, end );1175 name = tr_xmlFindTagContents( sub, end, "name" );1176 sub = tr_xmlFindTagContents( sub, end, "argumentList" );1177 if( !tr_xmlVerifyContents( name, end, getcmd->name, 1 ) )1178 {1179 if( parseScpdArgs( sub, end, getcmd, 'i' ) ||1180 parseScpdArgs( sub, end, getcmd, 'o' ) )1181 {1182 return 1;1183 }1184 }1185 else if( !tr_xmlVerifyContents( name, end, addcmd->name, 1 ) )1186 {1187 if( parseScpdArgs( sub, end, addcmd, 'i' ) )1188 {1189 return 1;1190 }1191 }1192 else if( !tr_xmlVerifyContents( name, end, delcmd->name, 1 ) )1193 {1194 if( parseScpdArgs( sub, end, delcmd, 'i' ) )1195 {1196 return 1;1197 }1198 }1199 next = tr_xmlSkipTag( next, end );1200 }1201 1202 return 0;1203 }1204 1205 static int1206 parseScpdArgs( const char * buf, const char * end,1207 struct upnp_act * action, char dir )1208 {1209 const char * sub, * which;1210 char * name, * var;1211 1212 assert( 'i' == dir || 'o' == dir );1213 which = ( 'i' == dir ? "in" : "out" );1214 1215 while( NULL != buf )1216 {1217 sub = tr_xmlFindTagContents( buf, end, "argument" );1218 if( !tr_xmlFindTagVerifyContents( sub, end, "direction", which, 1 ) )1219 {1220 name = tr_xmlDupTagContents( sub, end, "name" );1221 var = tr_xmlDupTagContents( sub, end, "relatedStateVariable" );1222 if( NULL == name || NULL == var )1223 {1224 free( name );1225 free( var );1226 }1227 else if( actionAdd( action, name, var, dir ) )1228 {1229 return 1;1230 }1231 }1232 buf = tr_xmlSkipTag( buf, end );1233 }1234 1235 return 0;1236 }1237 1238 static int1239 parseMapping( struct upnp_dev * dev, const char * buf, int len )1240 {1241 const char * end, * down, * next, * var;1242 int varlen, pret, cret, eret;1243 char * val;1244 1245 assert( 0 < dev->mappedport );1246 1247 if( NULL == dev->myaddr )1248 {1249 return -1;1250 }1251 1252 pret = -1;1253 cret = -1;1254 eret = -1;1255 1256 end = buf + len;1257 down = buf;1258 down = tr_xmlFindTagContents( down, end, "Envelope" );1259 down = tr_xmlFindTagContents( down, end, "Body" );1260 down = tr_xmlFindTagContents( down, end,1261 "GetSpecificPortMappingEntryResponse" );1262 1263 next = down;1264 while( NULL != next )1265 {1266 var = tr_xmlTagName( next, end, &varlen );1267 var = actionLookupVar( &dev->getcmd, var, varlen, 'o' );1268 if( NULL != var )1269 {1270 val = tr_xmlDupContents( tr_xmlTagContents( next, end ), end );1271 if( 0 == tr_strcasecmp( "InternalPort", var ) )1272 {1273 pret = ( strtol( val, NULL, 10 ) == dev->mappedport ? 1 : 0 );1274 }1275 else if( 0 == tr_strcasecmp( "InternalClient", var ) )1276 {1277 cret = ( 0 == strcmp( dev->myaddr, val ) ? 1 : 0 );1278 }1279 else if( 0 == tr_strcasecmp( "PortMappingEnabled", var ) )1280 {1281 eret = ( 0 == strcmp( "1", val ) ? 1 : 0 );1282 }1283 free( val );1284 }1285 next = tr_xmlSkipTag( next, end );1286 }1287 1288 return MIN( MIN( pret, cret), eret );1289 }1290 1291 static char *1292 joinstrs( const char * first, const char * delim, const char * second )1293 {1294 char * ret;1295 int len1, len2, len3;1296 1297 len1 = strlen( first );1298 len2 = strlen( delim );1299 len3 = strlen( second );1300 ret = malloc( len1 + len2 + len3 + 1 );1301 if( NULL == ret )1302 {1303 return NULL;1304 }1305 1306 memcpy( ret, first, len1 );1307 memcpy( ret + len1, delim, len2 );1308 memcpy( ret + len1 + len2, second, len3 );1309 ret[len1 + len2 + len3] = '\0';1310 1311 return ret;1312 }1313 1314 static tr_http_t *1315 soapRequest( int retry, const char * host, int port, const char * path,1316 const char * type, struct upnp_act * action, ... )1317 {1318 tr_http_t * http;1319 va_list ap;1320 const char * name, * value;1321 int method;1322 char * actstr;1323 1324 method = ( retry ? TR_HTTP_M_POST : TR_HTTP_POST );1325 http = makeHttp( method, host, port, path );1326 if( NULL != http )1327 {1328 tr_httpAddHeader( http, "Content-type",1329 "text/xml; encoding=\"utf-8\"" );1330 actstr = NULL;1331 asprintf( &actstr, "\"%s#%s\"", type, action->name );1332 tr_httpAddHeader( http, "SOAPAction", actstr );1333 free( actstr );1334 if( retry )1335 {1336 tr_httpAddHeader( http, "Man", "\"" SOAP_ENVELOPE "\"" );1337 }1338 tr_httpAddBody( http,1339 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"1340 "<s:Envelope"1341 " xmlns:s=\"" SOAP_ENVELOPE "\""1342 " s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"1343 " <s:Body>"1344 " <u:%s xmlns:u=\"%s\">", action->name, type );1345 1346 va_start( ap, action );1347 do1348 {1349 name = va_arg( ap, const char * );1350 value = NULL;1351 name = actionLookupName( action, name, -1, 'i' );1352 if( NULL != name )1353 {1354 value = va_arg( ap, const char * );1355 if( NULL != value )1356 {1357 tr_httpAddBody( http,1358 " <%s>%s</%s>", name, value, name );1359 }1360 else1361 {1362 tr_httpAddBody( http,1363 " <%s></%s>", name, name );1364 }1365 }1366 }1367 while( NULL != name && NULL != value );1368 va_end( ap );1369 1370 tr_httpAddBody( http,1371 " </u:%s>"1372 " </s:Body>"1373 "</s:Envelope>", action->name );1374 }1375 1376 return http;1377 }1378 1379 static void1380 actionSetup( struct upnp_act * action, const char * name, int prealloc )1381 {1382 action->name = strdup( name );1383 assert( NULL == action->args );1384 action->args = malloc( sizeof( *action->args ) * prealloc );1385 memset( action->args, 0, sizeof( *action->args ) * prealloc );1386 action->len = prealloc;1387 }1388 1389 static void1390 actionFree( struct upnp_act * act )1391 {1392 free( act->name );1393 while( 0 < act->len )1394 {1395 act->len--;1396 free( act->args[act->len].name );1397 free( act->args[act->len].var );1398 }1399 free( act->args );1400 }1401 1402 static int1403 actionAdd( struct upnp_act * act, char * name, char * var, char dir )1404 {1405 int ii;1406 void * newbuf;1407 1408 assert( 'i' == dir || 'o' == dir );1409 1410 ii = 0;1411 while( ii < act->len && NULL != act->args[ii].name )1412 {1413 ii++;1414 }1415 1416 if( ii == act->len )1417 {1418 newbuf = realloc( act->args, sizeof( *act->args ) * ( act->len + 1 ) );1419 if( NULL == newbuf )1420 {1421 return 1;1422 }1423 act->args = newbuf;1424 act->len++;1425 }1426 1427 act->args[ii].name = name;1428 act->args[ii].var = var;1429 act->args[ii].dir = dir;1430 1431 return 0;1432 }1433 1434 static const char *1435 actionLookup( struct upnp_act * act, const char * key, int len,1436 char dir, int getname )1437 {1438 int ii;1439 1440 assert( 'i' == dir || 'o' == dir );1441 1442 if( NULL == key || 0 == len )1443 {1444 return NULL;1445 }1446 if( 0 > len )1447 {1448 len = strlen( key );1449 }1450 1451 for( ii = 0; ii < act->len; ii++ )1452 {1453 if( NULL != act->args[ii].name && dir == act->args[ii].dir )1454 {1455 if( !getname &&1456 0 == tr_strncasecmp( act->args[ii].name, key, len ) )1457 {1458 return act->args[ii].var;1459 }1460 else if( getname &&1461 0 == tr_strncasecmp( act->args[ii].var, key, len ) )1462 {1463 return act->args[ii].name;1464 }1465 }1466 }1467 1468 return NULL;1469 } -
trunk/libtransmission/upnp.h
r2898 r3731 26 26 #define TR_UPNP_H 1 27 27 28 typedef struct tr_upnp _s tr_upnp_t;28 typedef struct tr_upnp tr_upnp; 29 29 30 tr_upnp _t* tr_upnpInit ( void );31 void tr_upnpStart ( tr_upnp_t* );32 void tr_upnpStop ( tr_upnp_t* );33 int tr_upnpStatus ( tr_upnp_t* );34 void tr_upnpForwardPort ( tr_upnp_t*, int );35 void tr_upnpRemoveForwarding ( tr_upnp_t* );36 void tr_upnpPulse ( tr_upnp_t* );37 void tr_upnpClose ( tr_upnp_t* );30 tr_upnp * tr_upnpInit ( void ); 31 void tr_upnpStart ( tr_upnp * ); 32 void tr_upnpStop ( tr_upnp * ); 33 int tr_upnpStatus ( tr_upnp * ); 34 void tr_upnpForwardPort ( tr_upnp *, int ); 35 void tr_upnpRemoveForwarding ( tr_upnp * ); 36 void tr_upnpPulse ( tr_upnp * ); 37 void tr_upnpClose ( tr_upnp * ); 38 38 39 39 #endif -
trunk/libtransmission/utils.c
r3666 r3731 296 296 ***/ 297 297 298 void * tr_memmem ( const void *vbig, size_t big_len,299 const void *vlittle, size_t little_len )300 {301 const char *big = vbig;302 const char *little = vlittle;303 size_t ii, jj;304 305 if( 0 == big_len || 0 == little_len )306 {307 return NULL;308 }309 310 for( ii = 0; ii + little_len <= big_len; ii++ )311 {312 for( jj = 0; jj < little_len; jj++ )313 {314 if( big[ii + jj] != little[jj] )315 {316 break;317 }318 }319 if( jj == little_len )320 {321 return (char*)big + ii;322 }323 }324 325 return NULL;326 }327 328 /**329 ***330 **/331 332 298 int 333 299 tr_compareUint16( uint16_t a, uint16_t b ) … … 475 441 tr_free( path ); 476 442 return 0; 477 }478 479 int480 tr_strncasecmp( const char * s1, const char * s2, size_t n )481 {482 if ( !n )483 return 0;484 485 while( n-- != 0 && tolower( *s1 ) == tolower( *s2 ) ) {486 if( !n || !*s1 || !*s2 )487 break;488 ++s1;489 ++s2;490 }491 492 return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);493 443 } 494 444 -
trunk/libtransmission/utils.h
r3650 r3731 43 43 int tr_rand ( int ); 44 44 45 void * tr_memmem( const void *, size_t, const void *, size_t );46 47 45 /*********************************************************************** 48 46 * tr_mkdirp … … 56 54 57 55 uint8_t* tr_loadFile( const char * filename, size_t * size ); 58 59 /***********************************************************************60 * tr_strcasecmp61 ***********************************************************************62 * A case-insensitive strncmp()63 **********************************************************************/64 #define tr_strcasecmp( ff, ss ) ( tr_strncasecmp( (ff), (ss), ULONG_MAX ) )65 int tr_strncasecmp( const char * first, const char * second, size_t len );66 56 67 57 /*********************************************************************** -
trunk/third-party/Makefile.am
r3528 r3731 1 SUBDIRS = libevent 1 SUBDIRS = libevent miniupnp 2 2 3 3 EXTRA_DIST = \ -
trunk/wx/Makefile.am
r3621 r3731 17 17 $(top_builddir)/libtransmission/libtransmission.a \ 18 18 $(top_builddir)/third-party/libevent/libevent.la \ 19 $(top_builddir)/third-party/miniupnp/libminiupnp.a \ 19 20 $(WX_LIBS) $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
Note: See TracChangeset
for help on using the changeset viewer.