Changeset 3731


Ignore:
Timestamp:
Nov 6, 2007, 4:02:50 PM (15 years ago)
Author:
charles
Message:

use Thomas Bernard's miniupnp library instead of rolling our own.

Location:
trunk
Files:
24 added
4 deleted
16 edited

Legend:

Unmodified
Added
Removed
  • trunk/cli/Makefile.am

    r3621 r3731  
    1111    $(top_builddir)/libtransmission/libtransmission.a \
    1212    $(top_builddir)/third-party/libevent/libevent.la \
     13    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    1314    $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
    1415
  • trunk/configure.ac

    r3729 r3731  
    208208                 libtransmission/Makefile
    209209                 third-party/Makefile
     210                 third-party/miniupnp/Makefile
    210211                 macosx/Makefile
    211212                 wx/Makefile
  • trunk/daemon/Makefile.am

    r3621 r3731  
    2929    ./libdaemon.a \
    3030    $(top_builddir)/libtransmission/libtransmission.a \
     31    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    3132    $(top_builddir)/third-party/libevent/libevent.la \
    3233    $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
  • trunk/gtk/Makefile.am

    r3621 r3731  
    5656    $(top_builddir)/libtransmission/libtransmission.a \
    5757    $(top_builddir)/third-party/libevent/libevent.la \
     58    $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    5859    $(GTK_LIBS) \
    5960    $(OPENSSL_LIBS) \
  • trunk/libtransmission/Makefile.am

    r3652 r3731  
    1 AM_CPPFLAGS = -I. -I$(top_srcdir) -D__TRANSMISSION__ $(LIBEVENT_CPPFLAGS)
     1AM_CPPFLAGS = -I. -I$(top_srcdir) -I$(top_srcdir)/third-party/ -D__TRANSMISSION__ $(LIBEVENT_CPPFLAGS)
    22AM_CFLAGS = $(OPENSSL_CFLAGS) $(PTHREAD_CFLAGS)
    33
     
    1212    fdlimit.c \
    1313    handshake.c \
    14     http.c \
    1514    inout.c \
    1615    ipcparse.c \
     
    3534    trevent.c \
    3635    upnp.c \
    37     utils.c \
    38     xml.c
     36    utils.c
    3937
    4038noinst_HEADERS = \
     
    4745    fdlimit.h \
    4846    handshake.h \
    49     http.h \
    5047    inout.h \
    5148    internal.h \
     
    7067    trevent.h \
    7168    upnp.h \
    72     utils.h \
    73     xml.h
     69    utils.h
    7470
    7571EXTRA_libtransmission_a_SOURCES = \
  • trunk/libtransmission/metainfo.c

    r3473 r3731  
    3232#include <unistd.h> /* unlink, stat */
    3333
     34#include <miniupnp/miniwget.h> /* parseURL */
     35
    3436#include "transmission.h"
    3537#include "bencode.h"
    3638#include "crypto.h" /* tr_sha1 */
    37 #include "http.h" /* tr_httpParseUrl */
    3839#include "metainfo.h"
    3940#include "platform.h"
    4041#include "utils.h"
     42
     43
     44static int
     45tr_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}
    4166
    4267/***********************************************************************
  • trunk/libtransmission/natpmp.c

    r3707 r3731  
    8484} tr_natpmp_req_t;
    8585
    86 struct tr_natpmp_s
     86struct tr_natpmp
    8787{
    8888#define PMP_STATE_IDLE          1
     
    115115
    116116static void
    117 unmap( tr_natpmp_t * pmp );
     117unmap( tr_natpmp * pmp );
    118118static int
    119119checktime( tr_natpmp_uptime_t * uptime, uint32_t seen );
     
    127127resetreq( tr_natpmp_req_t * req );
    128128static tr_tristate_t
    129 pulsereq( tr_natpmp_t * req );
     129pulsereq( tr_natpmp * req );
    130130static int
    131131sendreq( tr_natpmp_req_t * req );
     
    133133mcastsetup();
    134134static void
    135 mcastpulse( tr_natpmp_t * pmp );
     135mcastpulse( tr_natpmp * pmp );
    136136static tr_tristate_t
    137137parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse );
    138138
    139 tr_natpmp_t *
     139tr_natpmp *
    140140tr_natpmpInit()
    141141{
    142     tr_natpmp_t * pmp;
     142    tr_natpmp * pmp;
    143143
    144144    pmp = calloc( 1, sizeof( *pmp ) );
     
    171171
    172172void
    173 tr_natpmpStart( tr_natpmp_t * pmp )
     173tr_natpmpStart( tr_natpmp * pmp )
    174174{
    175175    if( !pmp->active )
     
    185185
    186186void
    187 tr_natpmpStop( tr_natpmp_t * pmp )
     187tr_natpmpStop( tr_natpmp * pmp )
    188188{
    189189    if( pmp->active )
     
    197197
    198198int
    199 tr_natpmpStatus( tr_natpmp_t * pmp )
     199tr_natpmpStatus( tr_natpmp * pmp )
    200200{
    201201    int ret;
     
    240240
    241241void
    242 tr_natpmpForwardPort( tr_natpmp_t * pmp, int port )
     242tr_natpmpForwardPort( tr_natpmp * pmp, int port )
    243243{
    244244    tr_inf( "nat-pmp set port %i", port );
     
    247247
    248248void
    249 tr_natpmpRemoveForwarding( tr_natpmp_t * pmp )
     249tr_natpmpRemoveForwarding( tr_natpmp * pmp )
    250250{
    251251    tr_inf( "nat-pmp unset port" );
     
    255255
    256256void
    257 tr_natpmpClose( tr_natpmp_t * pmp )
     257tr_natpmpClose( tr_natpmp * pmp )
    258258{
    259259    /* try to send at least one delete request if we have a port mapping */
     
    266266
    267267void
    268 tr_natpmpPulse( tr_natpmp_t * pmp, int * publicPort )
     268tr_natpmpPulse( tr_natpmp * pmp, int * publicPort )
    269269{
    270270    if( 0 <= pmp->mcastfd )
     
    444444
    445445void
    446 unmap( tr_natpmp_t * pmp )
     446unmap( tr_natpmp * pmp )
    447447{
    448448    switch( pmp->state )
     
    568568
    569569static tr_tristate_t
    570 pulsereq( tr_natpmp_t * pmp )
     570pulsereq( tr_natpmp * pmp )
    571571{
    572572    tr_natpmp_req_t  * req = pmp->req;
     
    693693
    694694static void
    695 mcastpulse( tr_natpmp_t * pmp )
     695mcastpulse( tr_natpmp * pmp )
    696696{
    697697    struct sockaddr_in sin;
  • trunk/libtransmission/natpmp.h

    r1720 r3731  
    2626#define TR_NATPMP_H 1
    2727
    28 typedef struct tr_natpmp_s tr_natpmp_t;
     28typedef struct tr_natpmp tr_natpmp;
    2929
    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 * );
     30tr_natpmp * tr_natpmpInit();
     31void        tr_natpmpStart( tr_natpmp * );
     32void        tr_natpmpStop( tr_natpmp * );
     33int         tr_natpmpStatus( tr_natpmp * );
     34void        tr_natpmpForwardPort( tr_natpmp *, int );
     35void        tr_natpmpRemoveForwarding( tr_natpmp * );
     36void        tr_natpmpPulse( tr_natpmp *, int * );
     37void        tr_natpmpClose( tr_natpmp * );
    3838
    3939#endif
  • trunk/libtransmission/shared.c

    r3265 r3731  
    5353
    5454    /* NAT-PMP/UPnP */
    55     tr_natpmp_t  * natpmp;
    56     tr_upnp_t    * upnp;
     55    tr_natpmp  * natpmp;
     56    tr_upnp    * upnp;
    5757};
    5858
  • trunk/libtransmission/tracker.c

    r3580 r3731  
    625625   
    626626    evbuffer_add_printf( buf, "%s"
    627                               "%sinfo_hash=%s"
     627                              "%cinfo_hash=%s"
    628628                              "&peer_id=%s"
    629629                              "&port=%d"
     
    640640                              "%s%s",
    641641        ann,
    642         ( strchr(ann, '?') == NULL ? "?" : "&" ),
     642        strchr(ann, '?') ? '&' : '?',
    643643        t->escaped,
    644644        t->peer_id,
  • trunk/libtransmission/upnp.c

    r3707 r3731  
    1 /******************************************************************************
    2  * $Id$
     1/*
     2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
    33 *
    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.
    59 *
    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 */
    2412
    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 */
    3314
    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>
    3918
    4019#include "transmission.h"
    41 #include "http.h"
    42 #include "net.h"
    43 #include "trcompat.h"
     20#include "internal.h"
     21#include "utils.h"
    4422#include "upnp.h"
    45 #include "utils.h"
    46 #include "xml.h"
    4723
    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
     24struct tr_upnp
    7525{
    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;
    7932};
    8033
    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**/
    11137
    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 *
     38tr_upnp*
    19039tr_upnpInit( void )
    19140{
    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 );
    19846    }
    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;
    21850}
    21951
    22052void
    221 tr_upnpStart( tr_upnp_t * upnp )
     53tr_upnpClose( tr_upnp * handle )
    22254{
    223     if( !upnp->active )
     55    tr_upnpStop( handle );
     56    FreeUPNPUrls( &handle->urls );
     57    tr_free( handle );
     58}
     59
     60/**
     61***
     62**/
     63
     64void
     65tr_upnpStart( tr_upnp * handle )
     66{
     67    handle->isEnabled = 1;
     68
     69    if( handle->port >= 0 )
    22470    {
    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") );
    23181    }
    23282}
    23383
    23484void
    235 tr_upnpStop( tr_upnp_t * upnp )
     85tr_upnpRemoveForwarding ( tr_upnp * handle )
    23686{
    237     if( upnp->active )
     87    handle->port = -1;
     88
     89    if( handle->isForwarding )
    23890    {
    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;
    243101    }
    244102}
    245103
    246 int
    247 tr_upnpStatus( tr_upnp_t * upnp )
     104void
     105tr_upnpForwardPort( tr_upnp * handle, int publicPort )
    248106{
    249     struct upnp_dev * ii;
    250     int               ret;
     107    tr_upnpRemoveForwarding( handle ); /* remove the old forwarding */
    251108
    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;
    277110
    278     return ret;
     111    if( handle->isEnabled )
     112        tr_upnpStart( handle );
    279113}
    280114
    281115void
    282 tr_upnpForwardPort( tr_upnp_t * upnp, int port )
     116tr_upnpStop( tr_upnp * handle )
    283117{
    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
     122int
     123tr_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;
    286132}
    287133
    288134void
    289 tr_upnpRemoveForwarding( tr_upnp_t * upnp )
     135tr_upnpPulse( tr_upnp * handle UNUSED )
    290136{
    291     tr_dbg( "upnp port unset" );
    292     upnp->port = 0;
     137    /* no-op */
    293138}
    294 
    295 void
    296 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_LOG
    308     if( NULL != vlog )
    309     {
    310         fclose( vlog );
    311     }
    312 #endif
    313 }
    314 
    315 void
    316 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     else
    349     {
    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             else
    359             {
    360                 devicePulse( *ii, 0 );
    361                 ii = &(*ii)->next;
    362             }
    363         }
    364     }
    365 }
    366 
    367 static int
    368 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_LOG
    403     fprintf( vlog, "send ssdp message, %i bytes:\n", len );
    404     fwrite( buf, 1, len, vlog );
    405     fputs( "\n\n", vlog );
    406     fflush( vlog );
    407 #endif
    408 
    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 int
    425 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 void
    441 killSock( int * sock )
    442 {
    443     if( 0 <= *sock )
    444     {
    445         tr_netClose( *sock );
    446         *sock = -1;
    447     }
    448 }
    449 
    450 static void
    451 killHttp( tr_http_t ** http )
    452 {
    453     tr_httpClose( *http );
    454     *http = NULL;
    455 }
    456 
    457 static int
    458 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 this
    468            { "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_t
    505 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     else
    523     {
    524 #ifdef VERBOSE_LOG
    525         fprintf( vlog, "receive ssdp message, %i bytes:\n", *len );
    526         fwrite( buf, 1, *len, vlog );
    527         fputs( "\n\n", vlog );
    528         fflush( vlog );
    529 #endif
    530         return TR_NET_OK;
    531     }
    532 }
    533 
    534 static int
    535 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             else
    554             {
    555                 tr_dbg( "found upnp ssdp notify request" );
    556             }
    557         }
    558         free( method );
    559         free( uri );
    560     }
    561     else
    562     {
    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 void
    591 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 void
    631 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 int
    657 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 int
    675 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             else
    748             {
    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             else
    769             {
    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             else
    793             {
    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             else
    834             {
    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             else
    851             {
    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     else
    883     {
    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_LOG
    895     const char * body;
    896     int          len;
    897 #endif
    898 
    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_LOG
    960     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 #endif
    972 
    973     return ret;
    974 }
    975 
    976 static int
    977 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_LOG
    1010     fprintf( vlog, "receive http message, %i bytes:\n", hlen );
    1011     fwrite( headers, 1, hlen, vlog );
    1012     fputs( "\n\n", vlog );
    1013     fflush( vlog );
    1014 #endif
    1015             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             else
    1036             {
    1037                 dev->soapretry = 1;
    1038             }
    1039             break;
    1040         case TR_NET_WAIT:
    1041             break;
    1042     }
    1043 
    1044     return -1;
    1045 }
    1046 
    1047 static int
    1048 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 and
    1104                    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     else
    1124     {
    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 void
    1140 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 int
    1160 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 int
    1206 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 int
    1239 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         do
    1348         {
    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                 else
    1361                 {
    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 void
    1380 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 void
    1390 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 int
    1403 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  
    2626#define TR_UPNP_H 1
    2727
    28 typedef struct tr_upnp_s tr_upnp_t;
     28typedef struct tr_upnp tr_upnp;
    2929
    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 * );
     30tr_upnp * tr_upnpInit             ( void );
     31void      tr_upnpStart            ( tr_upnp * );
     32void      tr_upnpStop             ( tr_upnp * );
     33int       tr_upnpStatus           ( tr_upnp * );
     34void      tr_upnpForwardPort      ( tr_upnp *, int );
     35void      tr_upnpRemoveForwarding ( tr_upnp * );
     36void      tr_upnpPulse            ( tr_upnp * );
     37void      tr_upnpClose            ( tr_upnp * );
    3838
    3939#endif
  • trunk/libtransmission/utils.c

    r3666 r3731  
    296296***/
    297297
    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 
    332298int
    333299tr_compareUint16( uint16_t a, uint16_t b )
     
    475441    tr_free( path );
    476442    return 0;
    477 }
    478 
    479 int
    480 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);
    493443}
    494444
  • trunk/libtransmission/utils.h

    r3650 r3731  
    4343int  tr_rand ( int );
    4444
    45 void * tr_memmem( const void *, size_t, const void *, size_t );
    46 
    4745/***********************************************************************
    4846 * tr_mkdirp
     
    5654
    5755uint8_t* tr_loadFile( const char * filename, size_t * size );
    58 
    59 /***********************************************************************
    60  * tr_strcasecmp
    61  ***********************************************************************
    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 );
    6656
    6757/***********************************************************************
  • trunk/third-party/Makefile.am

    r3528 r3731  
    1 SUBDIRS = libevent
     1SUBDIRS = libevent miniupnp
    22
    33EXTRA_DIST = \
  • trunk/wx/Makefile.am

    r3621 r3731  
    1717  $(top_builddir)/libtransmission/libtransmission.a \
    1818  $(top_builddir)/third-party/libevent/libevent.la \
     19  $(top_builddir)/third-party/miniupnp/libminiupnp.a \
    1920  $(WX_LIBS) $(OPENSSL_LIBS) $(PTHREAD_LIBS) -lm
Note: See TracChangeset for help on using the changeset viewer.