+++ /dev/null
-3 May 2014: dht-0.22
-
- * INCOMPATIBLE CHANGE: the callback now takes const arguments.
- * Consult the local storage when performing a search, which should
- make bootstrapping of tiny DHTs easier. Note that we're still not
- performing local stores, since that would require knowing our IP
- address.
- * Don't attempt to flush the debug stream if debugging is disabled.
- This appears to work around a bug in Transmission.
-
-25 July 2011: dht-0.21
-
- * Blacklisting support.
-
-7 July 2011: dht-0.20
-
- * Fix compilation on systems that have memmem but don't define HAVE_MEMMEM.
-
-30 April 2011: dht-0.19
-
- * Fix incorrect parsing of announces. Thanks to cjdelisle.
- * Relax rate limiting slightly.
-
-20 January 2011: dht-0.18
-
- * Fix a bug that could cause parse_message to enter an infinite loop
- on overflow. Thanks to Jordan Lee.
-
-9 January 2011: dht-0.17:
-
- * Fix a bug that prevented calling dht_init after dht_uninit.
- * Remove the "dofree" parameter to dht_uninit.
-
-23 December 2010: dht-0.16:
-
- * Change the interface to allow sharing of the UDP socket e.g. with uTP.
-
-1 July 2010: dht-0.15
-
- * Port to Windows, for the needs of Transmission.
-
-25 March 2010: dht-0.14
-
- * Fixed ordering of entries in parameter dictionaries.
-
-15 December 2009: dht-0.13
-
- * Implemented protection against incorrect addresses in the DHT.
- * Tweaked neighborhood maintenance to wake up less often.
-
-11 December 2009: dht-0.12
- * Fixed slightly incorrect formatting of DHT messages.
- * Fixed incorrect debugging message.
-
-22 November 2009: dht-0.11
-
- * Implemented IPv6 support (BEP-32).
- * Fixed a bug which could cause us to never mark a search as finished.
- * Fixed a bug that could cause us to send incomplete lists in response to
- find_nodes.
- * Limit the number of hashes that we're willing to track.
- * Made bucket maintenance slightly more aggressive.
- * Produce on-the-wire error messages to give a hint to the other side.
- * Added a bunch of options to dht-example to make it useful as
- a bootstrap node.
- * Send version "JC\0\0" when using dht-example.
-
-18 October 2009: dht-0.10
-
- * Send nodes even when sending values. This is a violation of the
- protocol, but I have been assured that it doesn't break any deployed
- implementation. This is also what both libtorrent and uTorrent do.
- * Give up immediately on a search peer when no token was provided. This
- is a very reasonable extension to the protocol, and certainly doesn't
- break anything.
- * Parse heterogeneous values lists correctly. This is mandated by BEP 32.
-
-20 September 2009: dht-0.9
-
- * Fixed incorrect computation of number of nodes.
- * Made the initial bucket split eagerly (speeds up bootstrapping).
- * Fixed initial filling of search buckets (speeds up searches).
-
-28 July 2009: dht-0.8
-
- * Fixed a crash when expiring the first search on the list.
- * Fixed freeing of the search list when uniniting with dofree = 1.
-
-24 June 2009: dht-0.7
-
- * Removed the fixed limit on the number of concurrent searches, we now
- use a linked list.
- * Fixed build on FreeBSD (thanks to Humihara and Charles Kerr).
-
-22 May 2009: dht-0.6
-
- * Fixed a buffer overflow (when reading) in parse_message.
- * Fixed slightly inacurrate metric computation when searching.
- * Removed a slightly inaccurate shortcut when responding to find_nodes.
- * Relaxed the rate-limiting parameters to 4 requests per second.
-
-19 May 2009: dht-0.5
-
- * Made reading of /dev/urandom a function provided by the user.
- * Implemented the ``v'' extension that identifies node implementations.
-
-18 May 2009: dht-0.4
-
- * Fixed the handling of tokens in announce_peer messages.
- * Implemented backtracking during search when nodes turn out to be dead.
-
-17 May 2009: dht-0.3
-
- * Fixed a number of incorrectly formatted messages.
- * Changed reply to find_peers to spread the load more uniformly.
- * Fixed a bug that could cause premature splitting.
- * Implemented rate limiting.
- * Changed some time constants to be less chatty.
- * When determining if a bucket is fresh enough, we now only take replies
- into account.
- * dht_get_nodes now returns nodes starting with our own bucket.
- * Tweaked the memory allocation strategy for stored peers.
-
-17 May 2009: dht-0.2
-
- * Fixed a crash in dht_uninit.
- * Added support for saving the list of known-good nodes.
- * Changed the interface of dht_nodes to provide the number of nodes that
- recently sent incoming requests.
-
-13 May 2009: dht-0.1
-
- * Initial public release.
+++ /dev/null
-Copyright (c) 2009, 2010 by Juliusz Chroboczek
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+++ /dev/null
-AM_CFLAGS = @PTHREAD_CFLAGS@
-
-noinst_LIBRARIES = libdht.a
-libdht_a_SOURCES = dht.c
-noinst_HEADERS = dht.h
-EXTRA_DIST = CHANGES dht-example.c LICENCE README
+++ /dev/null
-The files dht.c and dht.h implement the variant of the Kademlia Distributed
-Hash Table (DHT) used in the Bittorrent network (``mainline'' variant).
-
-The file dht-example.c is a stand-alone program that participates in the
-DHT. Another example is a patch against Transmission, which you might or
-might not be able to find somewhere.
-
-The code is designed to work well in both event-driven and threaded code.
-The caller, which is either an event-loop or a dedicated thread, must
-periodically call the function dht_periodic. In addition, it must call
-dht_periodic whenever any data has arrived from the network.
-
-All functions return -1 in case of failure, in which case errno is set, or
-a positive value in case of success.
-
-Initialisation
-**************
-
-* dht_init
-
-This must be called before using the library. You pass it a bound IPv4
-datagram socket, a bound IPv6 datagram socket, and your node id, a 20-octet
-array that should be globally unique.
-
-If you're on a multi-homed host, you should bind the sockets to one of your
-addresses.
-
-Node ids must be well distributed, so you cannot just use your Bittorrent
-id; you should either generate a truly random value (using plenty of
-entropy), or at least take the SHA-1 of something. However, it is a good
-idea to keep the id stable, so you may want to store it in stable storage
-at client shutdown.
-
-
-* dht_uninit
-
-This may be called at the end of the session.
-
-Bootstrapping
-*************
-
-The DHT needs to be taught a small number of contacts to begin functioning.
-You can hard-wire a small number of stable nodes in your application, but
-this obviously fails to scale. You may save the list of known good nodes
-at shutdown, and restore it at restart. You may also grab nodes from
-torrent files (the nodes field), and you may exchange contacts with other
-Bittorrent peers using the PORT extension.
-
-* dht_ping
-
-This is the main bootstrapping primitive. You pass it an address at which
-you believe that a DHT node may be living, and a query will be sent. If
-a node replies, and if there is space in the routing table, it will be
-inserted.
-
-* dht_insert_node
-
-This is a softer bootstrapping method, which doesn't actually send
-a query -- it only stores the node in the routing table for later use. It
-is a good idea to use that when e.g. restoring your routing table from
-disk.
-
-Note that dht_insert_node requires that you supply a node id. If the id
-turns out to be wrong, the DHT will eventually recover; still, inserting
-massive amounts of incorrect information into your routing table is
-certainly not a good idea.
-
-An additionaly difficulty with dht_insert_node is that, for various
-reasons, a Kademlia routing table cannot absorb nodes faster than a certain
-rate. Dumping a large number of nodes into a table using dht_insert_node
-will probably cause most of these nodes to be discarded straight away.
-(The tolerable rate is difficult to estimate; it is probably on the order
-of one node every few seconds per node already in the table divided by 8,
-for some suitable value of 8.)
-
-Doing some work
-***************
-
-* dht_periodic
-
-This function should be called by your main loop periodically, and also
-whenever data is available on the socket. The time after which
-dht_periodic should be called if no data is available is returned in the
-parameter tosleep. (You do not need to be particularly accurate; actually,
-it is a good idea to be late by a random value.)
-
-The parameters buf, buflen, from and fromlen optionally carry a received
-message. If buflen is 0, then no message was received.
-
-Dht_periodic also takes a callback, which will be called whenever something
-interesting happens (see below).
-
-* dht_search
-
-This schedules a search for information about the info-hash specified in
-id. If port is not 0, it specifies the TCP port on which the current peer
-is listening; in that case, when the search is complete it will be announced
-to the network. The port is in host order, beware if you got it from
-a struct sockaddr_in.
-
-In either case, data is passed to the callback function as soon as it is
-available, possibly in multiple pieces. The callback function will
-additionally be called when the search is complete.
-
-Up to DHT_MAX_SEARCHES (1024) searches can be in progress at a given time;
-any more, and dht_search will return -1. If you specify a new search for
-the same info hash as a search still in progress, the previous search is
-combined with the new one -- you will only receive a completion indication
-once.
-
-Information queries
-*******************
-
-* dht_nodes
-
-This returns the number of known good, dubious and cached nodes in our
-routing table. This can be used to decide whether it's reasonable to start
-a search; a search is likely to be successful as long as we have a few good
-nodes; however, in order to avoid overloading your bootstrap nodes, you may
-want to wait until good is at least 4 and good + doubtful is at least 30 or
-so.
-
-It also includes the number of nodes that recently send us an unsolicited
-request; this can be used to determine if the UDP port used for the DHT is
-firewalled.
-
-If you want to display a single figure to the user, you should display
-good + doubtful, which is the total number of nodes in your routing table.
-Some clients try to estimate the total number of nodes, but this doesn't
-make much sense -- since the result is exponential in the number of nodes
-in the routing table, small variations in the latter cause huge jumps in
-the former.
-
-* dht_get_nodes
-
-This retrieves the list of known good nodes, starting with the nodes in our
-own bucket. It is a good idea to save the list of known good nodes at
-shutdown, and ping them at startup.
-
-* dht_dump_tables
-* dht_debug
-
-These are debugging aids.
-
-Functions provided by you
-*************************
-
-* The callback function
-
-The callback function is called with 5 arguments. Closure is simply the
-value that you passed to dht_periodic. Event is one of DHT_EVENT_VALUES or
-DHT_EVENT_VALUES6, which indicates that we have new values, or
-DHT_EVENT_SEARCH_DONE or DHT_EVENT_SEARCH_DONE6, which indicates that
-a search has completed. In either case, info_hash is set to the info-hash
-of the search.
-
-In the case of DHT_EVENT_VALUES, data is a list of nodes in ``compact''
-format -- 6 or 18 bytes per node. Its length in bytes is in data_len.
-
-* dht_blacklisted
-
-This is a function that takes an IP address and returns true if this
-address should be silently ignored. Do not use this feature unless you
-really must -- Kademlia supposes transitive reachability.
-
-* dht_hash
-
-This should compute a reasonably strong cryptographic hash of the passed
-values. It should map cleanly to your favourite crypto toolkit's MD5 or
-SHA-1 function.
-
-* dht_random_bytes
-
-This should fill the supplied buffer with true random bytes.
-
-Final notes
-***********
-
-* NAT
-
-Nothing works well across NATs, but Kademlia is somewhat less impacted than
-many other protocols. The implementation takes care to distinguish between
-unidirectional and bidirectional reachability, and NATed nodes will
-eventually fall out from other nodes' routing tables.
-
-While there is no periodic pinging in this implementation, maintaining
-a full routing table requires slightly more than one packet exchange per
-minute, even in a completely idle network; this should be sufficient to
-make most full cone NATs happy.
-
-* Missing functionality
-
-Some of the code has had very little testing. If it breaks, you get to
-keep both pieces.
-
-
- Juliusz Chroboczek
- <jch@pps.jussieu.fr>
+++ /dev/null
-/* This example code was written by Juliusz Chroboczek.
- You are free to cut'n'paste from it to your heart's content. */
-
-/* For crypt */
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <sys/signal.h>
-
-#include "dht.h"
-
-#define MAX_BOOTSTRAP_NODES 20
-static struct sockaddr_storage bootstrap_nodes[MAX_BOOTSTRAP_NODES];
-static int num_bootstrap_nodes = 0;
-
-static volatile sig_atomic_t dumping = 0, searching = 0, exiting = 0;
-
-static void
-sigdump(int signo)
-{
- dumping = 1;
-}
-
-static void
-sigtest(int signo)
-{
- searching = 1;
-}
-
-static void
-sigexit(int signo)
-{
- exiting = 1;
-}
-
-static void
-init_signals(void)
-{
- struct sigaction sa;
- sigset_t ss;
-
- sigemptyset(&ss);
- sa.sa_handler = sigdump;
- sa.sa_mask = ss;
- sa.sa_flags = 0;
- sigaction(SIGUSR1, &sa, NULL);
-
- sigemptyset(&ss);
- sa.sa_handler = sigtest;
- sa.sa_mask = ss;
- sa.sa_flags = 0;
- sigaction(SIGUSR2, &sa, NULL);
-
- sigemptyset(&ss);
- sa.sa_handler = sigexit;
- sa.sa_mask = ss;
- sa.sa_flags = 0;
- sigaction(SIGINT, &sa, NULL);
-}
-
-const unsigned char hash[20] = {
- 0x54, 0x57, 0x87, 0x89, 0xdf, 0xc4, 0x23, 0xee, 0xf6, 0x03,
- 0x1f, 0x81, 0x94, 0xa9, 0x3a, 0x16, 0x98, 0x8b, 0x72, 0x7b
-};
-
-/* The call-back function is called by the DHT whenever something
- interesting happens. Right now, it only happens when we get a new value or
- when a search completes, but this may be extended in future versions. */
-static void
-callback(void *closure,
- int event,
- const unsigned char *info_hash,
- const void *data, size_t data_len)
-{
- if(event == DHT_EVENT_SEARCH_DONE)
- printf("Search done.\n");
- else if(event == DHT_EVENT_VALUES)
- printf("Received %d values.\n", (int)(data_len / 6));
-}
-
-static unsigned char buf[4096];
-
-int
-main(int argc, char **argv)
-{
- int i, rc, fd;
- int s = -1, s6 = -1, port;
- int have_id = 0;
- unsigned char myid[20];
- time_t tosleep = 0;
- char *id_file = "dht-example.id";
- int opt;
- int quiet = 0, ipv4 = 1, ipv6 = 1;
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
- struct sockaddr_storage from;
- socklen_t fromlen;
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
-
- memset(&sin6, 0, sizeof(sin6));
- sin6.sin6_family = AF_INET6;
-
-
-
- while(1) {
- opt = getopt(argc, argv, "q46b:i:");
- if(opt < 0)
- break;
-
- switch(opt) {
- case 'q': quiet = 1; break;
- case '4': ipv6 = 0; break;
- case '6': ipv4 = 0; break;
- case 'b': {
- char buf[16];
- int rc;
- rc = inet_pton(AF_INET, optarg, buf);
- if(rc == 1) {
- memcpy(&sin.sin_addr, buf, 4);
- break;
- }
- rc = inet_pton(AF_INET6, optarg, buf);
- if(rc == 1) {
- memcpy(&sin6.sin6_addr, buf, 16);
- break;
- }
- goto usage;
- }
- break;
- case 'i':
- id_file = optarg;
- break;
- default:
- goto usage;
- }
- }
-
- /* Ids need to be distributed evenly, so you cannot just use your
- bittorrent id. Either generate it randomly, or take the SHA-1 of
- something. */
- fd = open(id_file, O_RDONLY);
- if(fd >= 0) {
- rc = read(fd, myid, 20);
- if(rc == 20)
- have_id = 1;
- close(fd);
- }
-
- fd = open("/dev/urandom", O_RDONLY);
- if(fd < 0) {
- perror("open(random)");
- exit(1);
- }
-
- if(!have_id) {
- int ofd;
-
- rc = read(fd, myid, 20);
- if(rc < 0) {
- perror("read(random)");
- exit(1);
- }
- have_id = 1;
- close(fd);
-
- ofd = open(id_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if(ofd >= 0) {
- rc = write(ofd, myid, 20);
- if(rc < 20)
- unlink(id_file);
- close(ofd);
- }
- }
-
- {
- unsigned seed;
- read(fd, &seed, sizeof(seed));
- srandom(seed);
- }
-
- close(fd);
-
- if(argc < 2)
- goto usage;
-
- i = optind;
-
- if(argc < i + 1)
- goto usage;
-
- port = atoi(argv[i++]);
- if(port <= 0 || port >= 0x10000)
- goto usage;
-
- while(i < argc) {
- struct addrinfo hints, *info, *infop;
- memset(&hints, 0, sizeof(hints));
- hints.ai_socktype = SOCK_DGRAM;
- if(!ipv6)
- hints.ai_family = AF_INET;
- else if(!ipv4)
- hints.ai_family = AF_INET6;
- else
- hints.ai_family = 0;
- rc = getaddrinfo(argv[i], argv[i + 1], &hints, &info);
- if(rc != 0) {
- fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rc));
- exit(1);
- }
-
- i++;
- if(i >= argc)
- goto usage;
-
- infop = info;
- while(infop) {
- memcpy(&bootstrap_nodes[num_bootstrap_nodes],
- infop->ai_addr, infop->ai_addrlen);
- infop = infop->ai_next;
- num_bootstrap_nodes++;
- }
- freeaddrinfo(info);
-
- i++;
- }
-
- /* If you set dht_debug to a stream, every action taken by the DHT will
- be logged. */
- if(!quiet)
- dht_debug = stdout;
-
- /* We need an IPv4 and an IPv6 socket, bound to a stable port. Rumour
- has it that uTorrent works better when it is the same as your
- Bittorrent port. */
- if(ipv4) {
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if(s < 0) {
- perror("socket(IPv4)");
- }
- }
-
- if(ipv6) {
- s6 = socket(PF_INET6, SOCK_DGRAM, 0);
- if(s6 < 0) {
- perror("socket(IPv6)");
- }
- }
-
- if(s < 0 && s6 < 0) {
- fprintf(stderr, "Eek!");
- exit(1);
- }
-
-
- if(s >= 0) {
- sin.sin_port = htons(port);
- rc = bind(s, (struct sockaddr*)&sin, sizeof(sin));
- if(rc < 0) {
- perror("bind(IPv4)");
- exit(1);
- }
- }
-
- if(s6 >= 0) {
- int rc;
- int val = 1;
-
- rc = setsockopt(s6, IPPROTO_IPV6, IPV6_V6ONLY,
- (char *)&val, sizeof(val));
- if(rc < 0) {
- perror("setsockopt(IPV6_V6ONLY)");
- exit(1);
- }
-
- /* BEP-32 mandates that we should bind this socket to one of our
- global IPv6 addresses. In this simple example, this only
- happens if the user used the -b flag. */
-
- sin6.sin6_port = htons(port);
- rc = bind(s6, (struct sockaddr*)&sin6, sizeof(sin6));
- if(rc < 0) {
- perror("bind(IPv6)");
- exit(1);
- }
- }
-
- /* Init the dht. This sets the socket into non-blocking mode. */
- rc = dht_init(s, s6, myid, (unsigned char*)"JC\0\0");
- if(rc < 0) {
- perror("dht_init");
- exit(1);
- }
-
- init_signals();
-
- /* For bootstrapping, we need an initial list of nodes. This could be
- hard-wired, but can also be obtained from the nodes key of a torrent
- file, or from the PORT bittorrent message.
-
- Dht_ping_node is the brutal way of bootstrapping -- it actually
- sends a message to the peer. If you're going to bootstrap from
- a massive number of nodes (for example because you're restoring from
- a dump) and you already know their ids, it's better to use
- dht_insert_node. If the ids are incorrect, the DHT will recover. */
- for(i = 0; i < num_bootstrap_nodes; i++) {
- dht_ping_node((struct sockaddr*)&bootstrap_nodes[i],
- sizeof(bootstrap_nodes[i]));
- usleep(random() % 100000);
- }
-
- while(1) {
- struct timeval tv;
- fd_set readfds;
- tv.tv_sec = tosleep;
- tv.tv_usec = random() % 1000000;
-
- FD_ZERO(&readfds);
- if(s >= 0)
- FD_SET(s, &readfds);
- if(s6 >= 0)
- FD_SET(s6, &readfds);
- rc = select(s > s6 ? s + 1 : s6 + 1, &readfds, NULL, NULL, &tv);
- if(rc < 0) {
- if(errno != EINTR) {
- perror("select");
- sleep(1);
- }
- }
-
- if(exiting)
- break;
-
- if(rc > 0) {
- fromlen = sizeof(from);
- if(s >= 0 && FD_ISSET(s, &readfds))
- rc = recvfrom(s, buf, sizeof(buf) - 1, 0,
- (struct sockaddr*)&from, &fromlen);
- else if(s6 >= 0 && FD_ISSET(s6, &readfds))
- rc = recvfrom(s6, buf, sizeof(buf) - 1, 0,
- (struct sockaddr*)&from, &fromlen);
- else
- abort();
- }
-
- if(rc > 0) {
- buf[rc] = '\0';
- rc = dht_periodic(buf, rc, (struct sockaddr*)&from, fromlen,
- &tosleep, callback, NULL);
- } else {
- rc = dht_periodic(NULL, 0, NULL, 0, &tosleep, callback, NULL);
- }
- if(rc < 0) {
- if(errno == EINTR) {
- continue;
- } else {
- perror("dht_periodic");
- if(rc == EINVAL || rc == EFAULT)
- abort();
- tosleep = 1;
- }
- }
-
- /* This is how you trigger a search for a torrent hash. If port
- (the second argument) is non-zero, it also performs an announce.
- Since peers expire announced data after 30 minutes, it's a good
- idea to reannounce every 28 minutes or so. */
- if(searching) {
- if(s >= 0)
- dht_search(hash, 0, AF_INET, callback, NULL);
- if(s6 >= 0)
- dht_search(hash, 0, AF_INET6, callback, NULL);
- searching = 0;
- }
-
- /* For debugging, or idle curiosity. */
- if(dumping) {
- dht_dump_tables(stdout);
- dumping = 0;
- }
- }
-
- {
- struct sockaddr_in sin[500];
- struct sockaddr_in6 sin6[500];
- int num = 500, num6 = 500;
- int i;
- i = dht_get_nodes(sin, &num, sin6, &num6);
- printf("Found %d (%d + %d) good nodes.\n", i, num, num6);
- }
-
- dht_uninit();
- return 0;
-
- usage:
- printf("Usage: dht-example [-q] [-4] [-6] [-i filename] [-b address]...\n"
- " port [address port]...\n");
- exit(1);
-}
-
-/* Functions called by the DHT. */
-
-int
-dht_blacklisted(const struct sockaddr *sa, int salen)
-{
- return 0;
-}
-
-/* We need to provide a reasonably strong cryptographic hashing function.
- Here's how we'd do it if we had RSA's MD5 code. */
-#if 0
-void
-dht_hash(void *hash_return, int hash_size,
- const void *v1, int len1,
- const void *v2, int len2,
- const void *v3, int len3)
-{
- static MD5_CTX ctx;
- MD5Init(&ctx);
- MD5Update(&ctx, v1, len1);
- MD5Update(&ctx, v2, len2);
- MD5Update(&ctx, v3, len3);
- MD5Final(&ctx);
- if(hash_size > 16)
- memset((char*)hash_return + 16, 0, hash_size - 16);
- memcpy(hash_return, ctx.digest, hash_size > 16 ? 16 : hash_size);
-}
-#else
-/* But for this example, we might as well use something weaker. */
-void
-dht_hash(void *hash_return, int hash_size,
- const void *v1, int len1,
- const void *v2, int len2,
- const void *v3, int len3)
-{
- const char *c1 = v1, *c2 = v2, *c3 = v3;
- char key[9]; /* crypt is limited to 8 characters */
- int i;
-
- memset(key, 0, 9);
-#define CRYPT_HAPPY(c) ((c % 0x60) + 0x20)
-
- for(i = 0; i < 2 && i < len1; i++)
- key[i] = CRYPT_HAPPY(c1[i]);
- for(i = 0; i < 4 && i < len1; i++)
- key[2 + i] = CRYPT_HAPPY(c2[i]);
- for(i = 0; i < 2 && i < len1; i++)
- key[6 + i] = CRYPT_HAPPY(c3[i]);
- strncpy(hash_return, crypt(key, "jc"), hash_size);
-}
-#endif
-
-int
-dht_random_bytes(void *buf, size_t size)
-{
- int fd, rc, save;
-
- fd = open("/dev/urandom", O_RDONLY);
- if(fd < 0)
- return -1;
-
- rc = read(fd, buf, size);
-
- save = errno;
- close(fd);
- errno = save;
-
- return rc;
-}
+++ /dev/null
-/*
-Copyright (c) 2009-2011 by Juliusz Chroboczek
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
-/* Please, please, please.
-
- You are welcome to integrate this code in your favourite Bittorrent
- client. Please remember, however, that it is meant to be usable by
- others, including myself. This means no C++, no relicensing, and no
- gratuitious changes to the coding style. And please send back any
- improvements to the author. */
-
-/* For memmem. */
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/time.h>
-
-#ifndef WIN32
-#include <arpa/inet.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#else
-#include <w32api.h>
-#define WINVER WindowsXP
-#include <ws2tcpip.h>
-#endif
-
-#include "dht.h"
-
-#ifndef HAVE_MEMMEM
-#ifdef __GLIBC__
-#define HAVE_MEMMEM
-#endif
-#endif
-
-#ifndef MSG_CONFIRM
-#define MSG_CONFIRM 0
-#endif
-
-#ifdef WIN32
-
-#define EAFNOSUPPORT WSAEAFNOSUPPORT
-static int
-set_nonblocking(int fd, int nonblocking)
-{
- int rc;
-
- unsigned long mode = !!nonblocking;
- rc = ioctlsocket(fd, FIONBIO, &mode);
- if(rc != 0)
- errno = WSAGetLastError();
- return (rc == 0 ? 0 : -1);
-}
-
-static int
-random(void)
-{
- return rand();
-}
-extern const char *inet_ntop(int, const void *, char *, socklen_t);
-
-#else
-
-static int
-set_nonblocking(int fd, int nonblocking)
-{
- int rc;
- rc = fcntl(fd, F_GETFL, 0);
- if(rc < 0)
- return -1;
-
- rc = fcntl(fd, F_SETFL, nonblocking?(rc | O_NONBLOCK):(rc & ~O_NONBLOCK));
- if(rc < 0)
- return -1;
-
- return 0;
-}
-
-#endif
-
-/* We set sin_family to 0 to mark unused slots. */
-#if AF_INET == 0 || AF_INET6 == 0
-#error You lose
-#endif
-
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-/* nothing */
-#elif defined(__GNUC__)
-#define inline __inline
-#if (__GNUC__ >= 3)
-#define restrict __restrict
-#else
-#define restrict /**/
-#endif
-#else
-#define inline /**/
-#define restrict /**/
-#endif
-
-#define MAX(x, y) ((x) >= (y) ? (x) : (y))
-#define MIN(x, y) ((x) <= (y) ? (x) : (y))
-
-struct node {
- unsigned char id[20];
- struct sockaddr_storage ss;
- int sslen;
- time_t time; /* time of last message received */
- time_t reply_time; /* time of last correct reply received */
- time_t pinged_time; /* time of last request */
- int pinged; /* how many requests we sent since last reply */
- struct node *next;
-};
-
-struct bucket {
- int af;
- unsigned char first[20];
- int count; /* number of nodes */
- int time; /* time of last reply in this bucket */
- struct node *nodes;
- struct sockaddr_storage cached; /* the address of a likely candidate */
- int cachedlen;
- struct bucket *next;
-};
-
-struct search_node {
- unsigned char id[20];
- struct sockaddr_storage ss;
- int sslen;
- time_t request_time; /* the time of the last unanswered request */
- time_t reply_time; /* the time of the last reply */
- int pinged;
- unsigned char token[40];
- int token_len;
- int replied; /* whether we have received a reply */
- int acked; /* whether they acked our announcement */
-};
-
-/* When performing a search, we search for up to SEARCH_NODES closest nodes
- to the destination, and use the additional ones to backtrack if any of
- the target 8 turn out to be dead. */
-#define SEARCH_NODES 14
-
-struct search {
- unsigned short tid;
- int af;
- time_t step_time; /* the time of the last search_step */
- unsigned char id[20];
- unsigned short port; /* 0 for pure searches */
- int done;
- struct search_node nodes[SEARCH_NODES];
- int numnodes;
- struct search *next;
-};
-
-struct peer {
- time_t time;
- unsigned char ip[16];
- unsigned short len;
- unsigned short port;
-};
-
-/* The maximum number of peers we store for a given hash. */
-#ifndef DHT_MAX_PEERS
-#define DHT_MAX_PEERS 2048
-#endif
-
-/* The maximum number of hashes we're willing to track. */
-#ifndef DHT_MAX_HASHES
-#define DHT_MAX_HASHES 16384
-#endif
-
-/* The maximum number of searches we keep data about. */
-#ifndef DHT_MAX_SEARCHES
-#define DHT_MAX_SEARCHES 1024
-#endif
-
-/* The time after which we consider a search to be expirable. */
-#ifndef DHT_SEARCH_EXPIRE_TIME
-#define DHT_SEARCH_EXPIRE_TIME (62 * 60)
-#endif
-
-struct storage {
- unsigned char id[20];
- int numpeers, maxpeers;
- struct peer *peers;
- struct storage *next;
-};
-
-static struct storage * find_storage(const unsigned char *id);
-static void flush_search_node(struct search_node *n, struct search *sr);
-
-static int send_ping(const struct sockaddr *sa, int salen,
- const unsigned char *tid, int tid_len);
-static int send_pong(const struct sockaddr *sa, int salen,
- const unsigned char *tid, int tid_len);
-static int send_find_node(const struct sockaddr *sa, int salen,
- const unsigned char *tid, int tid_len,
- const unsigned char *target, int want, int confirm);
-static int send_nodes_peers(const struct sockaddr *sa, int salen,
- const unsigned char *tid, int tid_len,
- const unsigned char *nodes, int nodes_len,
- const unsigned char *nodes6, int nodes6_len,
- int af, struct storage *st,
- const unsigned char *token, int token_len);
-static int send_closest_nodes(const struct sockaddr *sa, int salen,
- const unsigned char *tid, int tid_len,
- const unsigned char *id, int want,
- int af, struct storage *st,
- const unsigned char *token, int token_len);
-static int send_get_peers(const struct sockaddr *sa, int salen,
- unsigned char *tid, int tid_len,
- unsigned char *infohash, int want, int confirm);
-static int send_announce_peer(const struct sockaddr *sa, int salen,
- unsigned char *tid, int tid_len,
- unsigned char *infohas, unsigned short port,
- unsigned char *token, int token_len, int confirm);
-static int send_peer_announced(const struct sockaddr *sa, int salen,
- unsigned char *tid, int tid_len);
-static int send_error(const struct sockaddr *sa, int salen,
- unsigned char *tid, int tid_len,
- int code, const char *message);
-
-#define ERROR 0
-#define REPLY 1
-#define PING 2
-#define FIND_NODE 3
-#define GET_PEERS 4
-#define ANNOUNCE_PEER 5
-
-#define WANT4 1
-#define WANT6 2
-
-static int parse_message(const unsigned char *buf, int buflen,
- unsigned char *tid_return, int *tid_len,
- unsigned char *id_return,
- unsigned char *info_hash_return,
- unsigned char *target_return,
- unsigned short *port_return,
- unsigned char *token_return, int *token_len,
- unsigned char *nodes_return, int *nodes_len,
- unsigned char *nodes6_return, int *nodes6_len,
- unsigned char *values_return, int *values_len,
- unsigned char *values6_return, int *values6_len,
- int *want_return);
-
-static const unsigned char zeroes[20] = {0};
-static const unsigned char ones[20] = {
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF
-};
-static const unsigned char v4prefix[16] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0
-};
-
-static int dht_socket = -1;
-static int dht_socket6 = -1;
-
-static time_t search_time;
-static time_t confirm_nodes_time;
-static time_t rotate_secrets_time;
-
-static unsigned char myid[20];
-static int have_v = 0;
-static unsigned char my_v[9];
-static unsigned char secret[8];
-static unsigned char oldsecret[8];
-
-static struct bucket *buckets = NULL;
-static struct bucket *buckets6 = NULL;
-static struct storage *storage;
-static int numstorage;
-
-static struct search *searches = NULL;
-static int numsearches;
-static unsigned short search_id;
-
-/* The maximum number of nodes that we snub. There is probably little
- reason to increase this value. */
-#ifndef DHT_MAX_BLACKLISTED
-#define DHT_MAX_BLACKLISTED 10
-#endif
-static struct sockaddr_storage blacklist[DHT_MAX_BLACKLISTED];
-int next_blacklisted;
-
-static struct timeval now;
-static time_t mybucket_grow_time, mybucket6_grow_time;
-static time_t expire_stuff_time;
-
-#define MAX_TOKEN_BUCKET_TOKENS 400
-static time_t token_bucket_time;
-static int token_bucket_tokens;
-
-FILE *dht_debug = NULL;
-
-#ifdef __GNUC__
- __attribute__ ((format (printf, 1, 2)))
-#endif
-static void
-debugf(const char *format, ...)
-{
- va_list args;
- va_start(args, format);
- if(dht_debug)
- vfprintf(dht_debug, format, args);
- va_end(args);
- if(dht_debug)
- fflush(dht_debug);
-}
-
-static void
-debug_printable(const unsigned char *buf, int buflen)
-{
- int i;
- if(dht_debug) {
- for(i = 0; i < buflen; i++)
- putc(buf[i] >= 32 && buf[i] <= 126 ? buf[i] : '.', dht_debug);
- }
-}
-
-static void
-print_hex(FILE *f, const unsigned char *buf, int buflen)
-{
- int i;
- for(i = 0; i < buflen; i++)
- fprintf(f, "%02x", buf[i]);
-}
-
-static int
-is_martian(const struct sockaddr *sa)
-{
- switch(sa->sa_family) {
- case AF_INET: {
- struct sockaddr_in *sin = (struct sockaddr_in*)sa;
- const unsigned char *address = (const unsigned char*)&sin->sin_addr;
- return sin->sin_port == 0 ||
- (address[0] == 0) ||
- (address[0] == 127) ||
- ((address[0] & 0xE0) == 0xE0);
- }
- case AF_INET6: {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa;
- const unsigned char *address = (const unsigned char*)&sin6->sin6_addr;
- return sin6->sin6_port == 0 ||
- (address[0] == 0xFF) ||
- (address[0] == 0xFE && (address[1] & 0xC0) == 0x80) ||
- (memcmp(address, zeroes, 15) == 0 &&
- (address[15] == 0 || address[15] == 1)) ||
- (memcmp(address, v4prefix, 12) == 0);
- }
-
- default:
- return 0;
- }
-}
-
-/* Forget about the ``XOR-metric''. An id is just a path from the
- root of the tree, so bits are numbered from the start. */
-
-static int
-id_cmp(const unsigned char *restrict id1, const unsigned char *restrict id2)
-{
- /* Memcmp is guaranteed to perform an unsigned comparison. */
- return memcmp(id1, id2, 20);
-}
-
-/* Find the lowest 1 bit in an id. */
-static int
-lowbit(const unsigned char *id)
-{
- int i, j;
- for(i = 19; i >= 0; i--)
- if(id[i] != 0)
- break;
-
- if(i < 0)
- return -1;
-
- for(j = 7; j >= 0; j--)
- if((id[i] & (0x80 >> j)) != 0)
- break;
-
- return 8 * i + j;
-}
-
-/* Find how many bits two ids have in common. */
-static int
-common_bits(const unsigned char *id1, const unsigned char *id2)
-{
- int i, j;
- unsigned char xor;
- for(i = 0; i < 20; i++) {
- if(id1[i] != id2[i])
- break;
- }
-
- if(i == 20)
- return 160;
-
- xor = id1[i] ^ id2[i];
-
- j = 0;
- while((xor & 0x80) == 0) {
- xor <<= 1;
- j++;
- }
-
- return 8 * i + j;
-}
-
-/* Determine whether id1 or id2 is closer to ref */
-static int
-xorcmp(const unsigned char *id1, const unsigned char *id2,
- const unsigned char *ref)
-{
- int i;
- for(i = 0; i < 20; i++) {
- unsigned char xor1, xor2;
- if(id1[i] == id2[i])
- continue;
- xor1 = id1[i] ^ ref[i];
- xor2 = id2[i] ^ ref[i];
- if(xor1 < xor2)
- return -1;
- else
- return 1;
- }
- return 0;
-}
-
-/* We keep buckets in a sorted linked list. A bucket b ranges from
- b->first inclusive up to b->next->first exclusive. */
-static int
-in_bucket(const unsigned char *id, struct bucket *b)
-{
- return id_cmp(b->first, id) <= 0 &&
- (b->next == NULL || id_cmp(id, b->next->first) < 0);
-}
-
-static struct bucket *
-find_bucket(unsigned const char *id, int af)
-{
- struct bucket *b = af == AF_INET ? buckets : buckets6;
-
- if(b == NULL)
- return NULL;
-
- while(1) {
- if(b->next == NULL)
- return b;
- if(id_cmp(id, b->next->first) < 0)
- return b;
- b = b->next;
- }
-}
-
-static struct bucket *
-previous_bucket(struct bucket *b)
-{
- struct bucket *p = b->af == AF_INET ? buckets : buckets6;
-
- if(b == p)
- return NULL;
-
- while(1) {
- if(p->next == NULL)
- return NULL;
- if(p->next == b)
- return p;
- p = p->next;
- }
-}
-
-/* Every bucket contains an unordered list of nodes. */
-static struct node *
-find_node(const unsigned char *id, int af)
-{
- struct bucket *b = find_bucket(id, af);
- struct node *n;
-
- if(b == NULL)
- return NULL;
-
- n = b->nodes;
- while(n) {
- if(id_cmp(n->id, id) == 0)
- return n;
- n = n->next;
- }
- return NULL;
-}
-
-/* Return a random node in a bucket. */
-static struct node *
-random_node(struct bucket *b)
-{
- struct node *n;
- int nn;
-
- if(b->count == 0)
- return NULL;
-
- nn = random() % b->count;
- n = b->nodes;
- while(nn > 0 && n) {
- n = n->next;
- nn--;
- }
- return n;
-}
-
-/* Return the middle id of a bucket. */
-static int
-bucket_middle(struct bucket *b, unsigned char *id_return)
-{
- int bit1 = lowbit(b->first);
- int bit2 = b->next ? lowbit(b->next->first) : -1;
- int bit = MAX(bit1, bit2) + 1;
-
- if(bit >= 160)
- return -1;
-
- memcpy(id_return, b->first, 20);
- id_return[bit / 8] |= (0x80 >> (bit % 8));
- return 1;
-}
-
-/* Return a random id within a bucket. */
-static int
-bucket_random(struct bucket *b, unsigned char *id_return)
-{
- int bit1 = lowbit(b->first);
- int bit2 = b->next ? lowbit(b->next->first) : -1;
- int bit = MAX(bit1, bit2) + 1;
- int i;
-
- if(bit >= 160) {
- memcpy(id_return, b->first, 20);
- return 1;
- }
-
- memcpy(id_return, b->first, bit / 8);
- id_return[bit / 8] = b->first[bit / 8] & (0xFF00 >> (bit % 8));
- id_return[bit / 8] |= random() & 0xFF >> (bit % 8);
- for(i = bit / 8 + 1; i < 20; i++)
- id_return[i] = random() & 0xFF;
- return 1;
-}
-
-/* Insert a new node into a bucket. */
-static struct node *
-insert_node(struct node *node)
-{
- struct bucket *b = find_bucket(node->id, node->ss.ss_family);
-
- if(b == NULL)
- return NULL;
-
- node->next = b->nodes;
- b->nodes = node;
- b->count++;
- return node;
-}
-
-/* This is our definition of a known-good node. */
-static int
-node_good(struct node *node)
-{
- return
- node->pinged <= 2 &&
- node->reply_time >= now.tv_sec - 7200 &&
- node->time >= now.tv_sec - 900;
-}
-
-/* Our transaction-ids are 4-bytes long, with the first two bytes identi-
- fying the kind of request, and the remaining two a sequence number in
- host order. */
-
-static void
-make_tid(unsigned char *tid_return, const char *prefix, unsigned short seqno)
-{
- tid_return[0] = prefix[0] & 0xFF;
- tid_return[1] = prefix[1] & 0xFF;
- memcpy(tid_return + 2, &seqno, 2);
-}
-
-static int
-tid_match(const unsigned char *tid, const char *prefix,
- unsigned short *seqno_return)
-{
- if(tid[0] == (prefix[0] & 0xFF) && tid[1] == (prefix[1] & 0xFF)) {
- if(seqno_return)
- memcpy(seqno_return, tid + 2, 2);
- return 1;
- } else
- return 0;
-}
-
-/* Every bucket caches the address of a likely node. Ping it. */
-static int
-send_cached_ping(struct bucket *b)
-{
- unsigned char tid[4];
- int rc;
- /* We set family to 0 when there's no cached node. */
- if(b->cached.ss_family == 0)
- return 0;
-
- debugf("Sending ping to cached node.\n");
- make_tid(tid, "pn", 0);
- rc = send_ping((struct sockaddr*)&b->cached, b->cachedlen, tid, 4);
- b->cached.ss_family = 0;
- b->cachedlen = 0;
- return rc;
-}
-
-/* Called whenever we send a request to a node, increases the ping count
- and, if that reaches 3, sends a ping to a new candidate. */
-static void
-pinged(struct node *n, struct bucket *b)
-{
- n->pinged++;
- n->pinged_time = now.tv_sec;
- if(n->pinged >= 3)
- send_cached_ping(b ? b : find_bucket(n->id, n->ss.ss_family));
-}
-
-/* The internal blacklist is an LRU cache of nodes that have sent
- incorrect messages. */
-static void
-blacklist_node(const unsigned char *id, const struct sockaddr *sa, int salen)
-{
- int i;
-
- debugf("Blacklisting broken node.\n");
-
- if(id) {
- struct node *n;
- struct search *sr;
- /* Make the node easy to discard. */
- n = find_node(id, sa->sa_family);
- if(n) {
- n->pinged = 3;
- pinged(n, NULL);
- }
- /* Discard it from any searches in progress. */
- sr = searches;
- while(sr) {
- for(i = 0; i < sr->numnodes; i++)
- if(id_cmp(sr->nodes[i].id, id) == 0)
- flush_search_node(&sr->nodes[i], sr);
- sr = sr->next;
- }
- }
- /* And make sure we don't hear from it again. */
- memcpy(&blacklist[next_blacklisted], sa, salen);
- next_blacklisted = (next_blacklisted + 1) % DHT_MAX_BLACKLISTED;
-}
-
-static int
-node_blacklisted(const struct sockaddr *sa, int salen)
-{
- int i;
-
- if((unsigned)salen > sizeof(struct sockaddr_storage))
- abort();
-
- if(dht_blacklisted(sa, salen))
- return 1;
-
- for(i = 0; i < DHT_MAX_BLACKLISTED; i++) {
- if(memcmp(&blacklist[i], sa, salen) == 0)
- return 1;
- }
-
- return 0;
-}
-
-/* Split a bucket into two equal parts. */
-static struct bucket *
-split_bucket(struct bucket *b)
-{
- struct bucket *new;
- struct node *nodes;
- int rc;
- unsigned char new_id[20];
-
- rc = bucket_middle(b, new_id);
- if(rc < 0)
- return NULL;
-
- new = calloc(1, sizeof(struct bucket));
- if(new == NULL)
- return NULL;
-
- new->af = b->af;
-
- send_cached_ping(b);
-
- memcpy(new->first, new_id, 20);
- new->time = b->time;
-
- nodes = b->nodes;
- b->nodes = NULL;
- b->count = 0;
- new->next = b->next;
- b->next = new;
- while(nodes) {
- struct node *n;
- n = nodes;
- nodes = nodes->next;
- insert_node(n);
- }
- return b;
-}
-
-/* We just learnt about a node, not necessarily a new one. Confirm is 1 if
- the node sent a message, 2 if it sent us a reply. */
-static struct node *
-new_node(const unsigned char *id, const struct sockaddr *sa, int salen,
- int confirm)
-{
- struct bucket *b = find_bucket(id, sa->sa_family);
- struct node *n;
- int mybucket, split;
-
- if(b == NULL)
- return NULL;
-
- if(id_cmp(id, myid) == 0)
- return NULL;
-
- if(is_martian(sa) || node_blacklisted(sa, salen))
- return NULL;
-
- mybucket = in_bucket(myid, b);
-
- if(confirm == 2)
- b->time = now.tv_sec;
-
- n = b->nodes;
- while(n) {
- if(id_cmp(n->id, id) == 0) {
- if(confirm || n->time < now.tv_sec - 15 * 60) {
- /* Known node. Update stuff. */
- memcpy((struct sockaddr*)&n->ss, sa, salen);
- if(confirm)
- n->time = now.tv_sec;
- if(confirm >= 2) {
- n->reply_time = now.tv_sec;
- n->pinged = 0;
- n->pinged_time = 0;
- }
- }
- return n;
- }
- n = n->next;
- }
-
- /* New node. */
-
- if(mybucket) {
- if(sa->sa_family == AF_INET)
- mybucket_grow_time = now.tv_sec;
- else
- mybucket6_grow_time = now.tv_sec;
- }
-
- /* First, try to get rid of a known-bad node. */
- n = b->nodes;
- while(n) {
- if(n->pinged >= 3 && n->pinged_time < now.tv_sec - 15) {
- memcpy(n->id, id, 20);
- memcpy((struct sockaddr*)&n->ss, sa, salen);
- n->time = confirm ? now.tv_sec : 0;
- n->reply_time = confirm >= 2 ? now.tv_sec : 0;
- n->pinged_time = 0;
- n->pinged = 0;
- return n;
- }
- n = n->next;
- }
-
- if(b->count >= 8) {
- /* Bucket full. Ping a dubious node */
- int dubious = 0;
- n = b->nodes;
- while(n) {
- /* Pick the first dubious node that we haven't pinged in the
- last 15 seconds. This gives nodes the time to reply, but
- tends to concentrate on the same nodes, so that we get rid
- of bad nodes fast. */
- if(!node_good(n)) {
- dubious = 1;
- if(n->pinged_time < now.tv_sec - 15) {
- unsigned char tid[4];
- debugf("Sending ping to dubious node.\n");
- make_tid(tid, "pn", 0);
- send_ping((struct sockaddr*)&n->ss, n->sslen,
- tid, 4);
- n->pinged++;
- n->pinged_time = now.tv_sec;
- break;
- }
- }
- n = n->next;
- }
-
- split = 0;
- if(mybucket) {
- if(!dubious)
- split = 1;
- /* If there's only one bucket, split eagerly. This is
- incorrect unless there's more than 8 nodes in the DHT. */
- else if(b->af == AF_INET && buckets->next == NULL)
- split = 1;
- else if(b->af == AF_INET6 && buckets6->next == NULL)
- split = 1;
- }
-
- if(split) {
- debugf("Splitting.\n");
- b = split_bucket(b);
- return new_node(id, sa, salen, confirm);
- }
-
- /* No space for this node. Cache it away for later. */
- if(confirm || b->cached.ss_family == 0) {
- memcpy(&b->cached, sa, salen);
- b->cachedlen = salen;
- }
-
- return NULL;
- }
-
- /* Create a new node. */
- n = calloc(1, sizeof(struct node));
- if(n == NULL)
- return NULL;
- memcpy(n->id, id, 20);
- memcpy(&n->ss, sa, salen);
- n->sslen = salen;
- n->time = confirm ? now.tv_sec : 0;
- n->reply_time = confirm >= 2 ? now.tv_sec : 0;
- n->next = b->nodes;
- b->nodes = n;
- b->count++;
- return n;
-}
-
-/* Called periodically to purge known-bad nodes. Note that we're very
- conservative here: broken nodes in the table don't do much harm, we'll
- recover as soon as we find better ones. */
-static int
-expire_buckets(struct bucket *b)
-{
- while(b) {
- struct node *n, *p;
- int changed = 0;
-
- while(b->nodes && b->nodes->pinged >= 4) {
- n = b->nodes;
- b->nodes = n->next;
- b->count--;
- changed = 1;
- free(n);
- }
-
- p = b->nodes;
- while(p) {
- while(p->next && p->next->pinged >= 4) {
- n = p->next;
- p->next = n->next;
- b->count--;
- changed = 1;
- free(n);
- }
- p = p->next;
- }
-
- if(changed)
- send_cached_ping(b);
-
- b = b->next;
- }
- expire_stuff_time = now.tv_sec + 120 + random() % 240;
- return 1;
-}
-
-/* While a search is in progress, we don't necessarily keep the nodes being
- walked in the main bucket table. A search in progress is identified by
- a unique transaction id, a short (and hence small enough to fit in the
- transaction id of the protocol packets). */
-
-static struct search *
-find_search(unsigned short tid, int af)
-{
- struct search *sr = searches;
- while(sr) {
- if(sr->tid == tid && sr->af == af)
- return sr;
- sr = sr->next;
- }
- return NULL;
-}
-
-/* A search contains a list of nodes, sorted by decreasing distance to the
- target. We just got a new candidate, insert it at the right spot or
- discard it. */
-
-static int
-insert_search_node(unsigned char *id,
- const struct sockaddr *sa, int salen,
- struct search *sr, int replied,
- unsigned char *token, int token_len)
-{
- struct search_node *n;
- int i, j;
-
- if(sa->sa_family != sr->af) {
- debugf("Attempted to insert node in the wrong family.\n");
- return 0;
- }
-
- for(i = 0; i < sr->numnodes; i++) {
- if(id_cmp(id, sr->nodes[i].id) == 0) {
- n = &sr->nodes[i];
- goto found;
- }
- if(xorcmp(id, sr->nodes[i].id, sr->id) < 0)
- break;
- }
-
- if(i == SEARCH_NODES)
- return 0;
-
- if(sr->numnodes < SEARCH_NODES)
- sr->numnodes++;
-
- for(j = sr->numnodes - 1; j > i; j--) {
- sr->nodes[j] = sr->nodes[j - 1];
- }
-
- n = &sr->nodes[i];
-
- memset(n, 0, sizeof(struct search_node));
- memcpy(n->id, id, 20);
-
-found:
- memcpy(&n->ss, sa, salen);
- n->sslen = salen;
-
- if(replied) {
- n->replied = 1;
- n->reply_time = now.tv_sec;
- n->request_time = 0;
- n->pinged = 0;
- }
- if(token) {
- if(token_len >= 40) {
- debugf("Eek! Overlong token.\n");
- } else {
- memcpy(n->token, token, token_len);
- n->token_len = token_len;
- }
- }
-
- return 1;
-}
-
-static void
-flush_search_node(struct search_node *n, struct search *sr)
-{
- int i = n - sr->nodes, j;
- for(j = i; j < sr->numnodes - 1; j++)
- sr->nodes[j] = sr->nodes[j + 1];
- sr->numnodes--;
-}
-
-static void
-expire_searches(void)
-{
- struct search *sr = searches, *previous = NULL;
-
- while(sr) {
- struct search *next = sr->next;
- if(sr->step_time < now.tv_sec - DHT_SEARCH_EXPIRE_TIME) {
- if(previous)
- previous->next = next;
- else
- searches = next;
- free(sr);
- numsearches--;
- } else {
- previous = sr;
- }
- sr = next;
- }
-}
-
-/* This must always return 0 or 1, never -1, not even on failure (see below). */
-static int
-search_send_get_peers(struct search *sr, struct search_node *n)
-{
- struct node *node;
- unsigned char tid[4];
-
- if(n == NULL) {
- int i;
- for(i = 0; i < sr->numnodes; i++) {
- if(sr->nodes[i].pinged < 3 && !sr->nodes[i].replied &&
- sr->nodes[i].request_time < now.tv_sec - 15)
- n = &sr->nodes[i];
- }
- }
-
- if(!n || n->pinged >= 3 || n->replied ||
- n->request_time >= now.tv_sec - 15)
- return 0;
-
- debugf("Sending get_peers.\n");
- make_tid(tid, "gp", sr->tid);
- send_get_peers((struct sockaddr*)&n->ss, n->sslen, tid, 4, sr->id, -1,
- n->reply_time >= now.tv_sec - 15);
- n->pinged++;
- n->request_time = now.tv_sec;
- /* If the node happens to be in our main routing table, mark it
- as pinged. */
- node = find_node(n->id, n->ss.ss_family);
- if(node) pinged(node, NULL);
- return 1;
-}
-
-/* When a search is in progress, we periodically call search_step to send
- further requests. */
-static void
-search_step(struct search *sr, dht_callback *callback, void *closure)
-{
- int i, j;
- int all_done = 1;
-
- /* Check if the first 8 live nodes have replied. */
- j = 0;
- for(i = 0; i < sr->numnodes && j < 8; i++) {
- struct search_node *n = &sr->nodes[i];
- if(n->pinged >= 3)
- continue;
- if(!n->replied) {
- all_done = 0;
- break;
- }
- j++;
- }
-
- if(all_done) {
- if(sr->port == 0) {
- goto done;
- } else {
- int all_acked = 1;
- j = 0;
- for(i = 0; i < sr->numnodes && j < 8; i++) {
- struct search_node *n = &sr->nodes[i];
- struct node *node;
- unsigned char tid[4];
- if(n->pinged >= 3)
- continue;
- /* A proposed extension to the protocol consists in
- omitting the token when storage tables are full. While
- I don't think this makes a lot of sense -- just sending
- a positive reply is just as good --, let's deal with it. */
- if(n->token_len == 0)
- n->acked = 1;
- if(!n->acked) {
- all_acked = 0;
- debugf("Sending announce_peer.\n");
- make_tid(tid, "ap", sr->tid);
- send_announce_peer((struct sockaddr*)&n->ss,
- sizeof(struct sockaddr_storage),
- tid, 4, sr->id, sr->port,
- n->token, n->token_len,
- n->reply_time >= now.tv_sec - 15);
- n->pinged++;
- n->request_time = now.tv_sec;
- node = find_node(n->id, n->ss.ss_family);
- if(node) pinged(node, NULL);
- }
- j++;
- }
- if(all_acked)
- goto done;
- }
- sr->step_time = now.tv_sec;
- return;
- }
-
- if(sr->step_time + 15 >= now.tv_sec)
- return;
-
- j = 0;
- for(i = 0; i < sr->numnodes; i++) {
- j += search_send_get_peers(sr, &sr->nodes[i]);
- if(j >= 3)
- break;
- }
- sr->step_time = now.tv_sec;
- return;
-
- done:
- sr->done = 1;
- if(callback)
- (*callback)(closure,
- sr->af == AF_INET ?
- DHT_EVENT_SEARCH_DONE : DHT_EVENT_SEARCH_DONE6,
- sr->id, NULL, 0);
- sr->step_time = now.tv_sec;
-}
-
-static struct search *
-new_search(void)
-{
- struct search *sr, *oldest = NULL;
-
- /* Find the oldest done search */
- sr = searches;
- while(sr) {
- if(sr->done &&
- (oldest == NULL || oldest->step_time > sr->step_time))
- oldest = sr;
- sr = sr->next;
- }
-
- /* The oldest slot is expired. */
- if(oldest && oldest->step_time < now.tv_sec - DHT_SEARCH_EXPIRE_TIME)
- return oldest;
-
- /* Allocate a new slot. */
- if(numsearches < DHT_MAX_SEARCHES) {
- sr = calloc(1, sizeof(struct search));
- if(sr != NULL) {
- sr->next = searches;
- searches = sr;
- numsearches++;
- return sr;
- }
- }
-
- /* Oh, well, never mind. Reuse the oldest slot. */
- return oldest;
-}
-
-/* Insert the contents of a bucket into a search structure. */
-static void
-insert_search_bucket(struct bucket *b, struct search *sr)
-{
- struct node *n;
- n = b->nodes;
- while(n) {
- insert_search_node(n->id, (struct sockaddr*)&n->ss, n->sslen,
- sr, 0, NULL, 0);
- n = n->next;
- }
-}
-
-/* Start a search. If port is non-zero, perform an announce when the
- search is complete. */
-int
-dht_search(const unsigned char *id, int port, int af,
- dht_callback *callback, void *closure)
-{
- struct search *sr;
- struct storage *st;
- struct bucket *b = find_bucket(id, af);
-
- if(b == NULL) {
- errno = EAFNOSUPPORT;
- return -1;
- }
-
- /* Try to answer this search locally. In a fully grown DHT this
- is very unlikely, but people are running modified versions of
- this code in private DHTs with very few nodes. What's wrong
- with flooding? */
- if(callback) {
- st = find_storage(id);
- if(st) {
- unsigned short swapped;
- unsigned char buf[18];
- int i;
-
- debugf("Found local data (%d peers).\n", st->numpeers);
-
- for(i = 0; i < st->numpeers; i++) {
- swapped = htons(st->peers[i].port);
- if(st->peers[i].len == 4) {
- memcpy(buf, st->peers[i].ip, 4);
- memcpy(buf + 4, &swapped, 2);
- (*callback)(closure, DHT_EVENT_VALUES, id,
- (void*)buf, 6);
- } else if(st->peers[i].len == 16) {
- memcpy(buf, st->peers[i].ip, 16);
- memcpy(buf + 16, &swapped, 2);
- (*callback)(closure, DHT_EVENT_VALUES6, id,
- (void*)buf, 18);
- }
- }
- }
- }
-
- sr = searches;
- while(sr) {
- if(sr->af == af && id_cmp(sr->id, id) == 0)
- break;
- sr = sr->next;
- }
-
- if(sr) {
- /* We're reusing data from an old search. Reusing the same tid
- means that we can merge replies for both searches. */
- int i;
- sr->done = 0;
- again:
- for(i = 0; i < sr->numnodes; i++) {
- struct search_node *n;
- n = &sr->nodes[i];
- /* Discard any doubtful nodes. */
- if(n->pinged >= 3 || n->reply_time < now.tv_sec - 7200) {
- flush_search_node(n, sr);
- goto again;
- }
- n->pinged = 0;
- n->token_len = 0;
- n->replied = 0;
- n->acked = 0;
- }
- } else {
- sr = new_search();
- if(sr == NULL) {
- errno = ENOSPC;
- return -1;
- }
- sr->af = af;
- sr->tid = search_id++;
- sr->step_time = 0;
- memcpy(sr->id, id, 20);
- sr->done = 0;
- sr->numnodes = 0;
- }
-
- sr->port = port;
-
- insert_search_bucket(b, sr);
-
- if(sr->numnodes < SEARCH_NODES) {
- struct bucket *p = previous_bucket(b);
- if(b->next)
- insert_search_bucket(b->next, sr);
- if(p)
- insert_search_bucket(p, sr);
- }
- if(sr->numnodes < SEARCH_NODES)
- insert_search_bucket(find_bucket(myid, af), sr);
-
- search_step(sr, callback, closure);
- search_time = now.tv_sec;
- return 1;
-}
-
-/* A struct storage stores all the stored peer addresses for a given info
- hash. */
-
-static struct storage *
-find_storage(const unsigned char *id)
-{
- struct storage *st = storage;
-
- while(st) {
- if(id_cmp(id, st->id) == 0)
- break;
- st = st->next;
- }
- return st;
-}
-
-static int
-storage_store(const unsigned char *id,
- const struct sockaddr *sa, unsigned short port)
-{
- int i, len;
- struct storage *st;
- unsigned char *ip;
-
- if(sa->sa_family == AF_INET) {
- struct sockaddr_in *sin = (struct sockaddr_in*)sa;
- ip = (unsigned char*)&sin->sin_addr;
- len = 4;
- } else if(sa->sa_family == AF_INET6) {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa;
- ip = (unsigned char*)&sin6->sin6_addr;
- len = 16;
- } else {
- return -1;
- }
-
- st = find_storage(id);
-
- if(st == NULL) {
- if(numstorage >= DHT_MAX_HASHES)
- return -1;
- st = calloc(1, sizeof(struct storage));
- if(st == NULL) return -1;
- memcpy(st->id, id, 20);
- st->next = storage;
- storage = st;
- numstorage++;
- }
-
- for(i = 0; i < st->numpeers; i++) {
- if(st->peers[i].port == port && st->peers[i].len == len &&
- memcmp(st->peers[i].ip, ip, len) == 0)
- break;
- }
-
- if(i < st->numpeers) {
- /* Already there, only need to refresh */
- st->peers[i].time = now.tv_sec;
- return 0;
- } else {
- struct peer *p;
- if(i >= st->maxpeers) {
- /* Need to expand the array. */
- struct peer *new_peers;
- int n;
- if(st->maxpeers >= DHT_MAX_PEERS)
- return 0;
- n = st->maxpeers == 0 ? 2 : 2 * st->maxpeers;
- n = MIN(n, DHT_MAX_PEERS);
- new_peers = realloc(st->peers, n * sizeof(struct peer));
- if(new_peers == NULL)
- return -1;
- st->peers = new_peers;
- st->maxpeers = n;
- }
- p = &st->peers[st->numpeers++];
- p->time = now.tv_sec;
- p->len = len;
- memcpy(p->ip, ip, len);
- p->port = port;
- return 1;
- }
-}
-
-static int
-expire_storage(void)
-{
- struct storage *st = storage, *previous = NULL;
- while(st) {
- int i = 0;
- while(i < st->numpeers) {
- if(st->peers[i].time < now.tv_sec - 32 * 60) {
- if(i != st->numpeers - 1)
- st->peers[i] = st->peers[st->numpeers - 1];
- st->numpeers--;
- } else {
- i++;
- }
- }
-
- if(st->numpeers == 0) {
- free(st->peers);
- if(previous)
- previous->next = st->next;
- else
- storage = st->next;
- free(st);
- if(previous)
- st = previous->next;
- else
- st = storage;
- numstorage--;
- if(numstorage < 0) {
- debugf("Eek... numstorage became negative.\n");
- numstorage = 0;
- }
- } else {
- previous = st;
- st = st->next;
- }
- }
- return 1;
-}
-
-static int
-rotate_secrets(void)
-{
- int rc;
-
- rotate_secrets_time = now.tv_sec + 900 + random() % 1800;
-
- memcpy(oldsecret, secret, sizeof(secret));
- rc = dht_random_bytes(secret, sizeof(secret));
-
- if(rc < 0)
- return -1;
-
- return 1;
-}
-
-#ifndef TOKEN_SIZE
-#define TOKEN_SIZE 8
-#endif
-
-static void
-make_token(const struct sockaddr *sa, int old, unsigned char *token_return)
-{
- void *ip;
- int iplen;
- unsigned short port;
-
- if(sa->sa_family == AF_INET) {
- struct sockaddr_in *sin = (struct sockaddr_in*)sa;
- ip = &sin->sin_addr;
- iplen = 4;
- port = htons(sin->sin_port);
- } else if(sa->sa_family == AF_INET6) {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa;
- ip = &sin6->sin6_addr;
- iplen = 16;
- port = htons(sin6->sin6_port);
- } else {
- abort();
- }
-
- dht_hash(token_return, TOKEN_SIZE,
- old ? oldsecret : secret, sizeof(secret),
- ip, iplen, (unsigned char*)&port, 2);
-}
-static int
-token_match(const unsigned char *token, int token_len,
- const struct sockaddr *sa)
-{
- unsigned char t[TOKEN_SIZE];
- if(token_len != TOKEN_SIZE)
- return 0;
- make_token(sa, 0, t);
- if(memcmp(t, token, TOKEN_SIZE) == 0)
- return 1;
- make_token(sa, 1, t);
- if(memcmp(t, token, TOKEN_SIZE) == 0)
- return 1;
- return 0;
-}
-
-int
-dht_nodes(int af, int *good_return, int *dubious_return, int *cached_return,
- int *incoming_return)
-{
- int good = 0, dubious = 0, cached = 0, incoming = 0;
- struct bucket *b = af == AF_INET ? buckets : buckets6;
-
- while(b) {
- struct node *n = b->nodes;
- while(n) {
- if(node_good(n)) {
- good++;
- if(n->time > n->reply_time)
- incoming++;
- } else {
- dubious++;
- }
- n = n->next;
- }
- if(b->cached.ss_family > 0)
- cached++;
- b = b->next;
- }
- if(good_return)
- *good_return = good;
- if(dubious_return)
- *dubious_return = dubious;
- if(cached_return)
- *cached_return = cached;
- if(incoming_return)
- *incoming_return = incoming;
- return good + dubious;
-}
-
-static void
-dump_bucket(FILE *f, struct bucket *b)
-{
- struct node *n = b->nodes;
- fprintf(f, "Bucket ");
- print_hex(f, b->first, 20);
- fprintf(f, " count %d age %d%s%s:\n",
- b->count, (int)(now.tv_sec - b->time),
- in_bucket(myid, b) ? " (mine)" : "",
- b->cached.ss_family ? " (cached)" : "");
- while(n) {
- char buf[512];
- unsigned short port;
- fprintf(f, " Node ");
- print_hex(f, n->id, 20);
- if(n->ss.ss_family == AF_INET) {
- struct sockaddr_in *sin = (struct sockaddr_in*)&n->ss;
- inet_ntop(AF_INET, &sin->sin_addr, buf, 512);
- port = ntohs(sin->sin_port);
- } else if(n->ss.ss_family == AF_INET6) {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&n->ss;
- inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 512);
- port = ntohs(sin6->sin6_port);
- } else {
- snprintf(buf, 512, "unknown(%d)", n->ss.ss_family);
- port = 0;
- }
-
- if(n->ss.ss_family == AF_INET6)
- fprintf(f, " [%s]:%d ", buf, port);
- else
- fprintf(f, " %s:%d ", buf, port);
- if(n->time != n->reply_time)
- fprintf(f, "age %ld, %ld",
- (long)(now.tv_sec - n->time),
- (long)(now.tv_sec - n->reply_time));
- else
- fprintf(f, "age %ld", (long)(now.tv_sec - n->time));
- if(n->pinged)
- fprintf(f, " (%d)", n->pinged);
- if(node_good(n))
- fprintf(f, " (good)");
- fprintf(f, "\n");
- n = n->next;
- }
-
-}
-
-void
-dht_dump_tables(FILE *f)
-{
- int i;
- struct bucket *b;
- struct storage *st = storage;
- struct search *sr = searches;
-
- fprintf(f, "My id ");
- print_hex(f, myid, 20);
- fprintf(f, "\n");
-
- b = buckets;
- while(b) {
- dump_bucket(f, b);
- b = b->next;
- }
-
- fprintf(f, "\n");
-
- b = buckets6;
- while(b) {
- dump_bucket(f, b);
- b = b->next;
- }
-
- while(sr) {
- fprintf(f, "\nSearch%s id ", sr->af == AF_INET6 ? " (IPv6)" : "");
- print_hex(f, sr->id, 20);
- fprintf(f, " age %d%s\n", (int)(now.tv_sec - sr->step_time),
- sr->done ? " (done)" : "");
- for(i = 0; i < sr->numnodes; i++) {
- struct search_node *n = &sr->nodes[i];
- fprintf(f, "Node %d id ", i);
- print_hex(f, n->id, 20);
- fprintf(f, " bits %d age ", common_bits(sr->id, n->id));
- if(n->request_time)
- fprintf(f, "%d, ", (int)(now.tv_sec - n->request_time));
- fprintf(f, "%d", (int)(now.tv_sec - n->reply_time));
- if(n->pinged)
- fprintf(f, " (%d)", n->pinged);
- fprintf(f, "%s%s.\n",
- find_node(n->id, AF_INET) ? " (known)" : "",
- n->replied ? " (replied)" : "");
- }
- sr = sr->next;
- }
-
- while(st) {
- fprintf(f, "\nStorage ");
- print_hex(f, st->id, 20);
- fprintf(f, " %d/%d nodes:", st->numpeers, st->maxpeers);
- for(i = 0; i < st->numpeers; i++) {
- char buf[100];
- if(st->peers[i].len == 4) {
- inet_ntop(AF_INET, st->peers[i].ip, buf, 100);
- } else if(st->peers[i].len == 16) {
- buf[0] = '[';
- inet_ntop(AF_INET6, st->peers[i].ip, buf + 1, 98);
- strcat(buf, "]");
- } else {
- strcpy(buf, "???");
- }
- fprintf(f, " %s:%u (%ld)",
- buf, st->peers[i].port,
- (long)(now.tv_sec - st->peers[i].time));
- }
- st = st->next;
- }
-
- fprintf(f, "\n\n");
- fflush(f);
-}
-
-int
-dht_init(int s, int s6, const unsigned char *id, const unsigned char *v)
-{
- int rc;
-
- if(dht_socket >= 0 || dht_socket6 >= 0 || buckets || buckets6) {
- errno = EBUSY;
- return -1;
- }
-
- searches = NULL;
- numsearches = 0;
-
- storage = NULL;
- numstorage = 0;
-
- if(s >= 0) {
- buckets = calloc(sizeof(struct bucket), 1);
- if(buckets == NULL)
- return -1;
- buckets->af = AF_INET;
-
- rc = set_nonblocking(s, 1);
- if(rc < 0)
- goto fail;
- }
-
- if(s6 >= 0) {
- buckets6 = calloc(sizeof(struct bucket), 1);
- if(buckets6 == NULL)
- return -1;
- buckets6->af = AF_INET6;
-
- rc = set_nonblocking(s6, 1);
- if(rc < 0)
- goto fail;
- }
-
- memcpy(myid, id, 20);
- if(v) {
- memcpy(my_v, "1:v4:", 5);
- memcpy(my_v + 5, v, 4);
- have_v = 1;
- } else {
- have_v = 0;
- }
-
- gettimeofday(&now, NULL);
-
- mybucket_grow_time = now.tv_sec;
- mybucket6_grow_time = now.tv_sec;
- confirm_nodes_time = now.tv_sec + random() % 3;
-
- search_id = random() & 0xFFFF;
- search_time = 0;
-
- next_blacklisted = 0;
-
- token_bucket_time = now.tv_sec;
- token_bucket_tokens = MAX_TOKEN_BUCKET_TOKENS;
-
- memset(secret, 0, sizeof(secret));
- rc = rotate_secrets();
- if(rc < 0)
- goto fail;
-
- dht_socket = s;
- dht_socket6 = s6;
-
- expire_buckets(buckets);
- expire_buckets(buckets6);
-
- return 1;
-
- fail:
- free(buckets);
- buckets = NULL;
- return -1;
-}
-
-int
-dht_uninit()
-{
- if(dht_socket < 0 && dht_socket6 < 0) {
- errno = EINVAL;
- return -1;
- }
-
- dht_socket = -1;
- dht_socket6 = -1;
-
- while(buckets) {
- struct bucket *b = buckets;
- buckets = b->next;
- while(b->nodes) {
- struct node *n = b->nodes;
- b->nodes = n->next;
- free(n);
- }
- free(b);
- }
-
- while(buckets6) {
- struct bucket *b = buckets6;
- buckets6 = b->next;
- while(b->nodes) {
- struct node *n = b->nodes;
- b->nodes = n->next;
- free(n);
- }
- free(b);
- }
-
- while(storage) {
- struct storage *st = storage;
- storage = storage->next;
- free(st->peers);
- free(st);
- }
-
- while(searches) {
- struct search *sr = searches;
- searches = searches->next;
- free(sr);
- }
-
- return 1;
-}
-
-/* Rate control for requests we receive. */
-
-static int
-token_bucket(void)
-{
- if(token_bucket_tokens == 0) {
- token_bucket_tokens = MIN(MAX_TOKEN_BUCKET_TOKENS,
- 100 * (now.tv_sec - token_bucket_time));
- token_bucket_time = now.tv_sec;
- }
-
- if(token_bucket_tokens == 0)
- return 0;
-
- token_bucket_tokens--;
- return 1;
-}
-
-static int
-neighbourhood_maintenance(int af)
-{
- unsigned char id[20];
- struct bucket *b = find_bucket(myid, af);
- struct bucket *q;
- struct node *n;
-
- if(b == NULL)
- return 0;
-
- memcpy(id, myid, 20);
- id[19] = random() & 0xFF;
- q = b;
- if(q->next && (q->count == 0 || (random() & 7) == 0))
- q = b->next;
- if(q->count == 0 || (random() & 7) == 0) {
- struct bucket *r;
- r = previous_bucket(b);
- if(r && r->count > 0)
- q = r;
- }
-
- if(q) {
- /* Since our node-id is the same in both DHTs, it's probably
- profitable to query both families. */
- int want = dht_socket >= 0 && dht_socket6 >= 0 ? (WANT4 | WANT6) : -1;
- n = random_node(q);
- if(n) {
- unsigned char tid[4];
- debugf("Sending find_node for%s neighborhood maintenance.\n",
- af == AF_INET6 ? " IPv6" : "");
- make_tid(tid, "fn", 0);
- send_find_node((struct sockaddr*)&n->ss, n->sslen,
- tid, 4, id, want,
- n->reply_time >= now.tv_sec - 15);
- pinged(n, q);
- }
- return 1;
- }
- return 0;
-}
-
-static int
-bucket_maintenance(int af)
-{
- struct bucket *b;
-
- b = af == AF_INET ? buckets : buckets6;
-
- while(b) {
- struct bucket *q;
- if(b->time < now.tv_sec - 600) {
- /* This bucket hasn't seen any positive confirmation for a long
- time. Pick a random id in this bucket's range, and send
- a request to a random node. */
- unsigned char id[20];
- struct node *n;
- int rc;
-
- rc = bucket_random(b, id);
- if(rc < 0)
- memcpy(id, b->first, 20);
-
- q = b;
- /* If the bucket is empty, we try to fill it from a neighbour.
- We also sometimes do it gratuitiously to recover from
- buckets full of broken nodes. */
- if(q->next && (q->count == 0 || (random() & 7) == 0))
- q = b->next;
- if(q->count == 0 || (random() & 7) == 0) {
- struct bucket *r;
- r = previous_bucket(b);
- if(r && r->count > 0)
- q = r;
- }
-
- if(q) {
- n = random_node(q);
- if(n) {
- unsigned char tid[4];
- int want = -1;
-
- if(dht_socket >= 0 && dht_socket6 >= 0) {
- struct bucket *otherbucket;
- otherbucket =
- find_bucket(id, af == AF_INET ? AF_INET6 : AF_INET);
- if(otherbucket && otherbucket->count < 8)
- /* The corresponding bucket in the other family
- is emptyish -- querying both is useful. */
- want = WANT4 | WANT6;
- else if(random() % 37 == 0)
- /* Most of the time, this just adds overhead.
- However, it might help stitch back one of
- the DHTs after a network collapse, so query
- both, but only very occasionally. */
- want = WANT4 | WANT6;
- }
-
- debugf("Sending find_node for%s bucket maintenance.\n",
- af == AF_INET6 ? " IPv6" : "");
- make_tid(tid, "fn", 0);
- send_find_node((struct sockaddr*)&n->ss, n->sslen,
- tid, 4, id, want,
- n->reply_time >= now.tv_sec - 15);
- pinged(n, q);
- /* In order to avoid sending queries back-to-back,
- give up for now and reschedule us soon. */
- return 1;
- }
- }
- }
- b = b->next;
- }
- return 0;
-}
-
-int
-dht_periodic(const void *buf, size_t buflen,
- const struct sockaddr *from, int fromlen,
- time_t *tosleep,
- dht_callback *callback, void *closure)
-{
- gettimeofday(&now, NULL);
-
- if(buflen > 0) {
- int message;
- unsigned char tid[16], id[20], info_hash[20], target[20];
- unsigned char nodes[256], nodes6[1024], token[128];
- int tid_len = 16, token_len = 128;
- int nodes_len = 256, nodes6_len = 1024;
- unsigned short port;
- unsigned char values[2048], values6[2048];
- int values_len = 2048, values6_len = 2048;
- int want;
- unsigned short ttid;
-
- if(is_martian(from))
- goto dontread;
-
- if(node_blacklisted(from, fromlen)) {
- debugf("Received packet from blacklisted node.\n");
- goto dontread;
- }
-
- if(((char*)buf)[buflen] != '\0') {
- debugf("Unterminated message.\n");
- errno = EINVAL;
- return -1;
- }
-
- message = parse_message(buf, buflen, tid, &tid_len, id, info_hash,
- target, &port, token, &token_len,
- nodes, &nodes_len, nodes6, &nodes6_len,
- values, &values_len, values6, &values6_len,
- &want);
-
- if(message < 0 || message == ERROR || id_cmp(id, zeroes) == 0) {
- debugf("Unparseable message: ");
- debug_printable(buf, buflen);
- debugf("\n");
- goto dontread;
- }
-
- if(id_cmp(id, myid) == 0) {
- debugf("Received message from self.\n");
- goto dontread;
- }
-
- if(message > REPLY) {
- /* Rate limit requests. */
- if(!token_bucket()) {
- debugf("Dropping request due to rate limiting.\n");
- goto dontread;
- }
- }
-
- switch(message) {
- case REPLY:
- if(tid_len != 4) {
- debugf("Broken node truncates transaction ids: ");
- debug_printable(buf, buflen);
- debugf("\n");
- /* This is really annoying, as it means that we will
- time-out all our searches that go through this node.
- Kill it. */
- blacklist_node(id, from, fromlen);
- goto dontread;
- }
- if(tid_match(tid, "pn", NULL)) {
- debugf("Pong!\n");
- new_node(id, from, fromlen, 2);
- } else if(tid_match(tid, "fn", NULL) ||
- tid_match(tid, "gp", NULL)) {
- int gp = 0;
- struct search *sr = NULL;
- if(tid_match(tid, "gp", &ttid)) {
- gp = 1;
- sr = find_search(ttid, from->sa_family);
- }
- debugf("Nodes found (%d+%d)%s!\n", nodes_len/26, nodes6_len/38,
- gp ? " for get_peers" : "");
- if(nodes_len % 26 != 0 || nodes6_len % 38 != 0) {
- debugf("Unexpected length for node info!\n");
- blacklist_node(id, from, fromlen);
- } else if(gp && sr == NULL) {
- debugf("Unknown search!\n");
- new_node(id, from, fromlen, 1);
- } else {
- int i;
- new_node(id, from, fromlen, 2);
- for(i = 0; i < nodes_len / 26; i++) {
- unsigned char *ni = nodes + i * 26;
- struct sockaddr_in sin;
- if(id_cmp(ni, myid) == 0)
- continue;
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- memcpy(&sin.sin_addr, ni + 20, 4);
- memcpy(&sin.sin_port, ni + 24, 2);
- new_node(ni, (struct sockaddr*)&sin, sizeof(sin), 0);
- if(sr && sr->af == AF_INET) {
- insert_search_node(ni,
- (struct sockaddr*)&sin,
- sizeof(sin),
- sr, 0, NULL, 0);
- }
- }
- for(i = 0; i < nodes6_len / 38; i++) {
- unsigned char *ni = nodes6 + i * 38;
- struct sockaddr_in6 sin6;
- if(id_cmp(ni, myid) == 0)
- continue;
- memset(&sin6, 0, sizeof(sin6));
- sin6.sin6_family = AF_INET6;
- memcpy(&sin6.sin6_addr, ni + 20, 16);
- memcpy(&sin6.sin6_port, ni + 36, 2);
- new_node(ni, (struct sockaddr*)&sin6, sizeof(sin6), 0);
- if(sr && sr->af == AF_INET6) {
- insert_search_node(ni,
- (struct sockaddr*)&sin6,
- sizeof(sin6),
- sr, 0, NULL, 0);
- }
- }
- if(sr)
- /* Since we received a reply, the number of
- requests in flight has decreased. Let's push
- another request. */
- search_send_get_peers(sr, NULL);
- }
- if(sr) {
- insert_search_node(id, from, fromlen, sr,
- 1, token, token_len);
- if(values_len > 0 || values6_len > 0) {
- debugf("Got values (%d+%d)!\n",
- values_len / 6, values6_len / 18);
- if(callback) {
- if(values_len > 0)
- (*callback)(closure, DHT_EVENT_VALUES, sr->id,
- (void*)values, values_len);
-
- if(values6_len > 0)
- (*callback)(closure, DHT_EVENT_VALUES6, sr->id,
- (void*)values6, values6_len);
- }
- }
- }
- } else if(tid_match(tid, "ap", &ttid)) {
- struct search *sr;
- debugf("Got reply to announce_peer.\n");
- sr = find_search(ttid, from->sa_family);
- if(!sr) {
- debugf("Unknown search!\n");
- new_node(id, from, fromlen, 1);
- } else {
- int i;
- new_node(id, from, fromlen, 2);
- for(i = 0; i < sr->numnodes; i++)
- if(id_cmp(sr->nodes[i].id, id) == 0) {
- sr->nodes[i].request_time = 0;
- sr->nodes[i].reply_time = now.tv_sec;
- sr->nodes[i].acked = 1;
- sr->nodes[i].pinged = 0;
- break;
- }
- /* See comment for gp above. */
- search_send_get_peers(sr, NULL);
- }
- } else {
- debugf("Unexpected reply: ");
- debug_printable(buf, buflen);
- debugf("\n");
- }
- break;
- case PING:
- debugf("Ping (%d)!\n", tid_len);
- new_node(id, from, fromlen, 1);
- debugf("Sending pong.\n");
- send_pong(from, fromlen, tid, tid_len);
- break;
- case FIND_NODE:
- debugf("Find node!\n");
- new_node(id, from, fromlen, 1);
- debugf("Sending closest nodes (%d).\n", want);
- send_closest_nodes(from, fromlen,
- tid, tid_len, target, want,
- 0, NULL, NULL, 0);
- break;
- case GET_PEERS:
- debugf("Get_peers!\n");
- new_node(id, from, fromlen, 1);
- if(id_cmp(info_hash, zeroes) == 0) {
- debugf("Eek! Got get_peers with no info_hash.\n");
- send_error(from, fromlen, tid, tid_len,
- 203, "Get_peers with no info_hash");
- break;
- } else {
- struct storage *st = find_storage(info_hash);
- unsigned char token[TOKEN_SIZE];
- make_token(from, 0, token);
- if(st && st->numpeers > 0) {
- debugf("Sending found%s peers.\n",
- from->sa_family == AF_INET6 ? " IPv6" : "");
- send_closest_nodes(from, fromlen,
- tid, tid_len,
- info_hash, want,
- from->sa_family, st,
- token, TOKEN_SIZE);
- } else {
- debugf("Sending nodes for get_peers.\n");
- send_closest_nodes(from, fromlen,
- tid, tid_len, info_hash, want,
- 0, NULL, token, TOKEN_SIZE);
- }
- }
- break;
- case ANNOUNCE_PEER:
- debugf("Announce peer!\n");
- new_node(id, from, fromlen, 1);
- if(id_cmp(info_hash, zeroes) == 0) {
- debugf("Announce_peer with no info_hash.\n");
- send_error(from, fromlen, tid, tid_len,
- 203, "Announce_peer with no info_hash");
- break;
- }
- if(!token_match(token, token_len, from)) {
- debugf("Incorrect token for announce_peer.\n");
- send_error(from, fromlen, tid, tid_len,
- 203, "Announce_peer with wrong token");
- break;
- }
- if(port == 0) {
- debugf("Announce_peer with forbidden port %d.\n", port);
- send_error(from, fromlen, tid, tid_len,
- 203, "Announce_peer with forbidden port number");
- break;
- }
- storage_store(info_hash, from, port);
- /* Note that if storage_store failed, we lie to the requestor.
- This is to prevent them from backtracking, and hence
- polluting the DHT. */
- debugf("Sending peer announced.\n");
- send_peer_announced(from, fromlen, tid, tid_len);
- }
- }
-
- dontread:
- if(now.tv_sec >= rotate_secrets_time)
- rotate_secrets();
-
- if(now.tv_sec >= expire_stuff_time) {
- expire_buckets(buckets);
- expire_buckets(buckets6);
- expire_storage();
- expire_searches();
- }
-
- if(search_time > 0 && now.tv_sec >= search_time) {
- struct search *sr;
- sr = searches;
- while(sr) {
- if(!sr->done && sr->step_time + 5 <= now.tv_sec) {
- search_step(sr, callback, closure);
- }
- sr = sr->next;
- }
-
- search_time = 0;
-
- sr = searches;
- while(sr) {
- if(!sr->done) {
- time_t tm = sr->step_time + 15 + random() % 10;
- if(search_time == 0 || search_time > tm)
- search_time = tm;
- }
- sr = sr->next;
- }
- }
-
- if(now.tv_sec >= confirm_nodes_time) {
- int soon = 0;
-
- soon |= bucket_maintenance(AF_INET);
- soon |= bucket_maintenance(AF_INET6);
-
- if(!soon) {
- if(mybucket_grow_time >= now.tv_sec - 150)
- soon |= neighbourhood_maintenance(AF_INET);
- if(mybucket6_grow_time >= now.tv_sec - 150)
- soon |= neighbourhood_maintenance(AF_INET6);
- }
-
- /* In order to maintain all buckets' age within 600 seconds, worst
- case is roughly 27 seconds, assuming the table is 22 bits deep.
- We want to keep a margin for neighborhood maintenance, so keep
- this within 25 seconds. */
- if(soon)
- confirm_nodes_time = now.tv_sec + 5 + random() % 20;
- else
- confirm_nodes_time = now.tv_sec + 60 + random() % 120;
- }
-
- if(confirm_nodes_time > now.tv_sec)
- *tosleep = confirm_nodes_time - now.tv_sec;
- else
- *tosleep = 0;
-
- if(search_time > 0) {
- if(search_time <= now.tv_sec)
- *tosleep = 0;
- else if(*tosleep > search_time - now.tv_sec)
- *tosleep = search_time - now.tv_sec;
- }
-
- return 1;
-}
-
-int
-dht_get_nodes(struct sockaddr_in *sin, int *num,
- struct sockaddr_in6 *sin6, int *num6)
-{
- int i, j;
- struct bucket *b;
- struct node *n;
-
- i = 0;
-
- /* For restoring to work without discarding too many nodes, the list
- must start with the contents of our bucket. */
- b = find_bucket(myid, AF_INET);
- if(b == NULL)
- goto no_ipv4;
-
- n = b->nodes;
- while(n && i < *num) {
- if(node_good(n)) {
- sin[i] = *(struct sockaddr_in*)&n->ss;
- i++;
- }
- n = n->next;
- }
-
- b = buckets;
- while(b && i < *num) {
- if(!in_bucket(myid, b)) {
- n = b->nodes;
- while(n && i < *num) {
- if(node_good(n)) {
- sin[i] = *(struct sockaddr_in*)&n->ss;
- i++;
- }
- n = n->next;
- }
- }
- b = b->next;
- }
-
- no_ipv4:
-
- j = 0;
-
- b = find_bucket(myid, AF_INET6);
- if(b == NULL)
- goto no_ipv6;
-
- n = b->nodes;
- while(n && j < *num6) {
- if(node_good(n)) {
- sin6[j] = *(struct sockaddr_in6*)&n->ss;
- j++;
- }
- n = n->next;
- }
-
- b = buckets6;
- while(b && j < *num6) {
- if(!in_bucket(myid, b)) {
- n = b->nodes;
- while(n && j < *num6) {
- if(node_good(n)) {
- sin6[j] = *(struct sockaddr_in6*)&n->ss;
- j++;
- }
- n = n->next;
- }
- }
- b = b->next;
- }
-
- no_ipv6:
-
- *num = i;
- *num6 = j;
- return i + j;
-}
-
-int
-dht_insert_node(const unsigned char *id, struct sockaddr *sa, int salen)
-{
- struct node *n;
-
- if(sa->sa_family != AF_INET) {
- errno = EAFNOSUPPORT;
- return -1;
- }
-
- n = new_node(id, (struct sockaddr*)sa, salen, 0);
- return !!n;
-}
-
-int
-dht_ping_node(struct sockaddr *sa, int salen)
-{
- unsigned char tid[4];
-
- debugf("Sending ping.\n");
- make_tid(tid, "pn", 0);
- return send_ping(sa, salen, tid, 4);
-}
-
-/* We could use a proper bencoding printer and parser, but the format of
- DHT messages is fairly stylised, so this seemed simpler. */
-
-#define CHECK(offset, delta, size) \
- if(delta < 0 || offset + delta > size) goto fail
-
-#define INC(offset, delta, size) \
- CHECK(offset, delta, size); \
- offset += delta
-
-#define COPY(buf, offset, src, delta, size) \
- CHECK(offset, delta, size); \
- memcpy(buf + offset, src, delta); \
- offset += delta;
-
-#define ADD_V(buf, offset, size) \
- if(have_v) { \
- COPY(buf, offset, my_v, sizeof(my_v), size); \
- }
-
-static int
-dht_send(const void *buf, size_t len, int flags,
- const struct sockaddr *sa, int salen)
-{
- int s;
-
- if(salen == 0)
- abort();
-
- if(node_blacklisted(sa, salen)) {
- debugf("Attempting to send to blacklisted node.\n");
- errno = EPERM;
- return -1;
- }
-
- if(sa->sa_family == AF_INET)
- s = dht_socket;
- else if(sa->sa_family == AF_INET6)
- s = dht_socket6;
- else
- s = -1;
-
- if(s < 0) {
- errno = EAFNOSUPPORT;
- return -1;
- }
-
- return sendto(s, buf, len, flags, sa, salen);
-}
-
-int
-send_ping(const struct sockaddr *sa, int salen,
- const unsigned char *tid, int tid_len)
-{
- char buf[512];
- int i = 0, rc;
- rc = snprintf(buf + i, 512 - i, "d1:ad2:id20:"); INC(i, rc, 512);
- COPY(buf, i, myid, 20, 512);
- rc = snprintf(buf + i, 512 - i, "e1:q4:ping1:t%d:", tid_len);
- INC(i, rc, 512);
- COPY(buf, i, tid, tid_len, 512);
- ADD_V(buf, i, 512);
- rc = snprintf(buf + i, 512 - i, "1:y1:qe"); INC(i, rc, 512);
- return dht_send(buf, i, 0, sa, salen);
-
- fail:
- errno = ENOSPC;
- return -1;
-}
-
-int
-send_pong(const struct sockaddr *sa, int salen,
- const unsigned char *tid, int tid_len)
-{
- char buf[512];
- int i = 0, rc;
- rc = snprintf(buf + i, 512 - i, "d1:rd2:id20:"); INC(i, rc, 512);
- COPY(buf, i, myid, 20, 512);
- rc = snprintf(buf + i, 512 - i, "e1:t%d:", tid_len); INC(i, rc, 512);
- COPY(buf, i, tid, tid_len, 512);
- ADD_V(buf, i, 512);
- rc = snprintf(buf + i, 512 - i, "1:y1:re"); INC(i, rc, 512);
- return dht_send(buf, i, 0, sa, salen);
-
- fail:
- errno = ENOSPC;
- return -1;
-}
-
-int
-send_find_node(const struct sockaddr *sa, int salen,
- const unsigned char *tid, int tid_len,
- const unsigned char *target, int want, int confirm)
-{
- char buf[512];
- int i = 0, rc;
- rc = snprintf(buf + i, 512 - i, "d1:ad2:id20:"); INC(i, rc, 512);
- COPY(buf, i, myid, 20, 512);
- rc = snprintf(buf + i, 512 - i, "6:target20:"); INC(i, rc, 512);
- COPY(buf, i, target, 20, 512);
- if(want > 0) {
- rc = snprintf(buf + i, 512 - i, "4:wantl%s%se",
- (want & WANT4) ? "2:n4" : "",
- (want & WANT6) ? "2:n6" : "");
- INC(i, rc, 512);
- }
- rc = snprintf(buf + i, 512 - i, "e1:q9:find_node1:t%d:", tid_len);
- INC(i, rc, 512);
- COPY(buf, i, tid, tid_len, 512);
- ADD_V(buf, i, 512);
- rc = snprintf(buf + i, 512 - i, "1:y1:qe"); INC(i, rc, 512);
- return dht_send(buf, i, confirm ? MSG_CONFIRM : 0, sa, salen);
-
- fail:
- errno = ENOSPC;
- return -1;
-}
-
-int
-send_nodes_peers(const struct sockaddr *sa, int salen,
- const unsigned char *tid, int tid_len,
- const unsigned char *nodes, int nodes_len,
- const unsigned char *nodes6, int nodes6_len,
- int af, struct storage *st,
- const unsigned char *token, int token_len)
-{
- char buf[2048];
- int i = 0, rc, j0, j, k, len;
-
- rc = snprintf(buf + i, 2048 - i, "d1:rd2:id20:"); INC(i, rc, 2048);
- COPY(buf, i, myid, 20, 2048);
- if(nodes_len > 0) {
- rc = snprintf(buf + i, 2048 - i, "5:nodes%d:", nodes_len);
- INC(i, rc, 2048);
- COPY(buf, i, nodes, nodes_len, 2048);
- }
- if(nodes6_len > 0) {
- rc = snprintf(buf + i, 2048 - i, "6:nodes6%d:", nodes6_len);
- INC(i, rc, 2048);
- COPY(buf, i, nodes6, nodes6_len, 2048);
- }
- if(token_len > 0) {
- rc = snprintf(buf + i, 2048 - i, "5:token%d:", token_len);
- INC(i, rc, 2048);
- COPY(buf, i, token, token_len, 2048);
- }
-
- if(st && st->numpeers > 0) {
- /* We treat the storage as a circular list, and serve a randomly
- chosen slice. In order to make sure we fit within 1024 octets,
- we limit ourselves to 50 peers. */
-
- len = af == AF_INET ? 4 : 16;
- j0 = random() % st->numpeers;
- j = j0;
- k = 0;
-
- rc = snprintf(buf + i, 2048 - i, "6:valuesl"); INC(i, rc, 2048);
- do {
- if(st->peers[j].len == len) {
- unsigned short swapped;
- swapped = htons(st->peers[j].port);
- rc = snprintf(buf + i, 2048 - i, "%d:", len + 2);
- INC(i, rc, 2048);
- COPY(buf, i, st->peers[j].ip, len, 2048);
- COPY(buf, i, &swapped, 2, 2048);
- k++;
- }
- j = (j + 1) % st->numpeers;
- } while(j != j0 && k < 50);
- rc = snprintf(buf + i, 2048 - i, "e"); INC(i, rc, 2048);
- }
-
- rc = snprintf(buf + i, 2048 - i, "e1:t%d:", tid_len); INC(i, rc, 2048);
- COPY(buf, i, tid, tid_len, 2048);
- ADD_V(buf, i, 2048);
- rc = snprintf(buf + i, 2048 - i, "1:y1:re"); INC(i, rc, 2048);
-
- return dht_send(buf, i, 0, sa, salen);
-
- fail:
- errno = ENOSPC;
- return -1;
-}
-
-static int
-insert_closest_node(unsigned char *nodes, int numnodes,
- const unsigned char *id, struct node *n)
-{
- int i, size;
-
- if(n->ss.ss_family == AF_INET)
- size = 26;
- else if(n->ss.ss_family == AF_INET6)
- size = 38;
- else
- abort();
-
- for(i = 0; i< numnodes; i++) {
- if(id_cmp(n->id, nodes + size * i) == 0)
- return numnodes;
- if(xorcmp(n->id, nodes + size * i, id) < 0)
- break;
- }
-
- if(i == 8)
- return numnodes;
-
- if(numnodes < 8)
- numnodes++;
-
- if(i < numnodes - 1)
- memmove(nodes + size * (i + 1), nodes + size * i,
- size * (numnodes - i - 1));
-
- if(n->ss.ss_family == AF_INET) {
- struct sockaddr_in *sin = (struct sockaddr_in*)&n->ss;
- memcpy(nodes + size * i, n->id, 20);
- memcpy(nodes + size * i + 20, &sin->sin_addr, 4);
- memcpy(nodes + size * i + 24, &sin->sin_port, 2);
- } else if(n->ss.ss_family == AF_INET6) {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&n->ss;
- memcpy(nodes + size * i, n->id, 20);
- memcpy(nodes + size * i + 20, &sin6->sin6_addr, 16);
- memcpy(nodes + size * i + 36, &sin6->sin6_port, 2);
- } else {
- abort();
- }
-
- return numnodes;
-}
-
-static int
-buffer_closest_nodes(unsigned char *nodes, int numnodes,
- const unsigned char *id, struct bucket *b)
-{
- struct node *n = b->nodes;
- while(n) {
- if(node_good(n))
- numnodes = insert_closest_node(nodes, numnodes, id, n);
- n = n->next;
- }
- return numnodes;
-}
-
-int
-send_closest_nodes(const struct sockaddr *sa, int salen,
- const unsigned char *tid, int tid_len,
- const unsigned char *id, int want,
- int af, struct storage *st,
- const unsigned char *token, int token_len)
-{
- unsigned char nodes[8 * 26];
- unsigned char nodes6[8 * 38];
- int numnodes = 0, numnodes6 = 0;
- struct bucket *b;
-
- if(want < 0)
- want = sa->sa_family == AF_INET ? WANT4 : WANT6;
-
- if((want & WANT4)) {
- b = find_bucket(id, AF_INET);
- if(b) {
- numnodes = buffer_closest_nodes(nodes, numnodes, id, b);
- if(b->next)
- numnodes = buffer_closest_nodes(nodes, numnodes, id, b->next);
- b = previous_bucket(b);
- if(b)
- numnodes = buffer_closest_nodes(nodes, numnodes, id, b);
- }
- }
-
- if((want & WANT6)) {
- b = find_bucket(id, AF_INET6);
- if(b) {
- numnodes6 = buffer_closest_nodes(nodes6, numnodes6, id, b);
- if(b->next)
- numnodes6 =
- buffer_closest_nodes(nodes6, numnodes6, id, b->next);
- b = previous_bucket(b);
- if(b)
- numnodes6 = buffer_closest_nodes(nodes6, numnodes6, id, b);
- }
- }
- debugf(" (%d+%d nodes.)\n", numnodes, numnodes6);
-
- return send_nodes_peers(sa, salen, tid, tid_len,
- nodes, numnodes * 26,
- nodes6, numnodes6 * 38,
- af, st, token, token_len);
-}
-
-int
-send_get_peers(const struct sockaddr *sa, int salen,
- unsigned char *tid, int tid_len, unsigned char *infohash,
- int want, int confirm)
-{
- char buf[512];
- int i = 0, rc;
-
- rc = snprintf(buf + i, 512 - i, "d1:ad2:id20:"); INC(i, rc, 512);
- COPY(buf, i, myid, 20, 512);
- rc = snprintf(buf + i, 512 - i, "9:info_hash20:"); INC(i, rc, 512);
- COPY(buf, i, infohash, 20, 512);
- if(want > 0) {
- rc = snprintf(buf + i, 512 - i, "4:wantl%s%se",
- (want & WANT4) ? "2:n4" : "",
- (want & WANT6) ? "2:n6" : "");
- INC(i, rc, 512);
- }
- rc = snprintf(buf + i, 512 - i, "e1:q9:get_peers1:t%d:", tid_len);
- INC(i, rc, 512);
- COPY(buf, i, tid, tid_len, 512);
- ADD_V(buf, i, 512);
- rc = snprintf(buf + i, 512 - i, "1:y1:qe"); INC(i, rc, 512);
- return dht_send(buf, i, confirm ? MSG_CONFIRM : 0, sa, salen);
-
- fail:
- errno = ENOSPC;
- return -1;
-}
-
-int
-send_announce_peer(const struct sockaddr *sa, int salen,
- unsigned char *tid, int tid_len,
- unsigned char *infohash, unsigned short port,
- unsigned char *token, int token_len, int confirm)
-{
- char buf[512];
- int i = 0, rc;
-
- rc = snprintf(buf + i, 512 - i, "d1:ad2:id20:"); INC(i, rc, 512);
- COPY(buf, i, myid, 20, 512);
- rc = snprintf(buf + i, 512 - i, "9:info_hash20:"); INC(i, rc, 512);
- COPY(buf, i, infohash, 20, 512);
- rc = snprintf(buf + i, 512 - i, "4:porti%ue5:token%d:", (unsigned)port,
- token_len);
- INC(i, rc, 512);
- COPY(buf, i, token, token_len, 512);
- rc = snprintf(buf + i, 512 - i, "e1:q13:announce_peer1:t%d:", tid_len);
- INC(i, rc, 512);
- COPY(buf, i, tid, tid_len, 512);
- ADD_V(buf, i, 512);
- rc = snprintf(buf + i, 512 - i, "1:y1:qe"); INC(i, rc, 512);
-
- return dht_send(buf, i, confirm ? 0 : MSG_CONFIRM, sa, salen);
-
- fail:
- errno = ENOSPC;
- return -1;
-}
-
-static int
-send_peer_announced(const struct sockaddr *sa, int salen,
- unsigned char *tid, int tid_len)
-{
- char buf[512];
- int i = 0, rc;
-
- rc = snprintf(buf + i, 512 - i, "d1:rd2:id20:"); INC(i, rc, 512);
- COPY(buf, i, myid, 20, 512);
- rc = snprintf(buf + i, 512 - i, "e1:t%d:", tid_len);
- INC(i, rc, 512);
- COPY(buf, i, tid, tid_len, 512);
- ADD_V(buf, i, 512);
- rc = snprintf(buf + i, 512 - i, "1:y1:re"); INC(i, rc, 512);
- return dht_send(buf, i, 0, sa, salen);
-
- fail:
- errno = ENOSPC;
- return -1;
-}
-
-static int
-send_error(const struct sockaddr *sa, int salen,
- unsigned char *tid, int tid_len,
- int code, const char *message)
-{
- char buf[512];
- int i = 0, rc;
-
- rc = snprintf(buf + i, 512 - i, "d1:eli%de%d:",
- code, (int)strlen(message));
- INC(i, rc, 512);
- COPY(buf, i, message, (int)strlen(message), 512);
- rc = snprintf(buf + i, 512 - i, "e1:t%d:", tid_len); INC(i, rc, 512);
- COPY(buf, i, tid, tid_len, 512);
- ADD_V(buf, i, 512);
- rc = snprintf(buf + i, 512 - i, "1:y1:ee"); INC(i, rc, 512);
- return dht_send(buf, i, 0, sa, salen);
-
- fail:
- errno = ENOSPC;
- return -1;
-}
-
-#undef CHECK
-#undef INC
-#undef COPY
-#undef ADD_V
-
-#ifdef HAVE_MEMMEM
-
-static void *
-dht_memmem(const void *haystack, size_t haystacklen,
- const void *needle, size_t needlelen)
-{
- return memmem(haystack, haystacklen, needle, needlelen);
-}
-
-#else
-
-static void *
-dht_memmem(const void *haystack, size_t haystacklen,
- const void *needle, size_t needlelen)
-{
- const char *h = haystack;
- const char *n = needle;
- size_t i;
-
- /* size_t is unsigned */
- if(needlelen > haystacklen)
- return NULL;
-
- for(i = 0; i <= haystacklen - needlelen; i++) {
- if(memcmp(h + i, n, needlelen) == 0)
- return (void*)(h + i);
- }
- return NULL;
-}
-
-#endif
-
-static int
-parse_message(const unsigned char *buf, int buflen,
- unsigned char *tid_return, int *tid_len,
- unsigned char *id_return, unsigned char *info_hash_return,
- unsigned char *target_return, unsigned short *port_return,
- unsigned char *token_return, int *token_len,
- unsigned char *nodes_return, int *nodes_len,
- unsigned char *nodes6_return, int *nodes6_len,
- unsigned char *values_return, int *values_len,
- unsigned char *values6_return, int *values6_len,
- int *want_return)
-{
- const unsigned char *p;
-
- /* This code will happily crash if the buffer is not NUL-terminated. */
- if(buf[buflen] != '\0') {
- debugf("Eek! parse_message with unterminated buffer.\n");
- return -1;
- }
-
-#define CHECK(ptr, len) \
- if(((unsigned char*)ptr) + (len) > (buf) + (buflen)) goto overflow;
-
- if(tid_return) {
- p = dht_memmem(buf, buflen, "1:t", 3);
- if(p) {
- long l;
- char *q;
- l = strtol((char*)p + 3, &q, 10);
- if(q && *q == ':' && l > 0 && l < *tid_len) {
- CHECK(q + 1, l);
- memcpy(tid_return, q + 1, l);
- *tid_len = l;
- } else
- *tid_len = 0;
- }
- }
- if(id_return) {
- p = dht_memmem(buf, buflen, "2:id20:", 7);
- if(p) {
- CHECK(p + 7, 20);
- memcpy(id_return, p + 7, 20);
- } else {
- memset(id_return, 0, 20);
- }
- }
- if(info_hash_return) {
- p = dht_memmem(buf, buflen, "9:info_hash20:", 14);
- if(p) {
- CHECK(p + 14, 20);
- memcpy(info_hash_return, p + 14, 20);
- } else {
- memset(info_hash_return, 0, 20);
- }
- }
- if(port_return) {
- p = dht_memmem(buf, buflen, "porti", 5);
- if(p) {
- long l;
- char *q;
- l = strtol((char*)p + 5, &q, 10);
- if(q && *q == 'e' && l > 0 && l < 0x10000)
- *port_return = l;
- else
- *port_return = 0;
- } else
- *port_return = 0;
- }
- if(target_return) {
- p = dht_memmem(buf, buflen, "6:target20:", 11);
- if(p) {
- CHECK(p + 11, 20);
- memcpy(target_return, p + 11, 20);
- } else {
- memset(target_return, 0, 20);
- }
- }
- if(token_return) {
- p = dht_memmem(buf, buflen, "5:token", 7);
- if(p) {
- long l;
- char *q;
- l = strtol((char*)p + 7, &q, 10);
- if(q && *q == ':' && l > 0 && l < *token_len) {
- CHECK(q + 1, l);
- memcpy(token_return, q + 1, l);
- *token_len = l;
- } else
- *token_len = 0;
- } else
- *token_len = 0;
- }
-
- if(nodes_len) {
- p = dht_memmem(buf, buflen, "5:nodes", 7);
- if(p) {
- long l;
- char *q;
- l = strtol((char*)p + 7, &q, 10);
- if(q && *q == ':' && l > 0 && l < *nodes_len) {
- CHECK(q + 1, l);
- memcpy(nodes_return, q + 1, l);
- *nodes_len = l;
- } else
- *nodes_len = 0;
- } else
- *nodes_len = 0;
- }
-
- if(nodes6_len) {
- p = dht_memmem(buf, buflen, "6:nodes6", 8);
- if(p) {
- long l;
- char *q;
- l = strtol((char*)p + 8, &q, 10);
- if(q && *q == ':' && l > 0 && l < *nodes6_len) {
- CHECK(q + 1, l);
- memcpy(nodes6_return, q + 1, l);
- *nodes6_len = l;
- } else
- *nodes6_len = 0;
- } else
- *nodes6_len = 0;
- }
-
- if(values_len || values6_len) {
- p = dht_memmem(buf, buflen, "6:valuesl", 9);
- if(p) {
- int i = p - buf + 9;
- int j = 0, j6 = 0;
- while(1) {
- long l;
- char *q;
- l = strtol((char*)buf + i, &q, 10);
- if(q && *q == ':' && l > 0) {
- CHECK(q + 1, l);
- i = q + 1 + l - (char*)buf;
- if(l == 6) {
- if(j + l > *values_len)
- continue;
- memcpy((char*)values_return + j, q + 1, l);
- j += l;
- } else if(l == 18) {
- if(j6 + l > *values6_len)
- continue;
- memcpy((char*)values6_return + j6, q + 1, l);
- j6 += l;
- } else {
- debugf("Received weird value -- %d bytes.\n", (int)l);
- }
- } else {
- break;
- }
- }
- if(i >= buflen || buf[i] != 'e')
- debugf("eek... unexpected end for values.\n");
- if(values_len)
- *values_len = j;
- if(values6_len)
- *values6_len = j6;
- } else {
- if(values_len)
- *values_len = 0;
- if(values6_len)
- *values6_len = 0;
- }
- }
-
- if(want_return) {
- p = dht_memmem(buf, buflen, "4:wantl", 7);
- if(p) {
- int i = p - buf + 7;
- *want_return = 0;
- while(buf[i] > '0' && buf[i] <= '9' && buf[i + 1] == ':' &&
- i + 2 + buf[i] - '0' < buflen) {
- CHECK(buf + i + 2, buf[i] - '0');
- if(buf[i] == '2' && memcmp(buf + i + 2, "n4", 2) == 0)
- *want_return |= WANT4;
- else if(buf[i] == '2' && memcmp(buf + i + 2, "n6", 2) == 0)
- *want_return |= WANT6;
- else
- debugf("eek... unexpected want flag (%c)\n", buf[i]);
- i += 2 + buf[i] - '0';
- }
- if(i >= buflen || buf[i] != 'e')
- debugf("eek... unexpected end for want.\n");
- } else {
- *want_return = -1;
- }
- }
-
-#undef CHECK
-
- if(dht_memmem(buf, buflen, "1:y1:r", 6))
- return REPLY;
- if(dht_memmem(buf, buflen, "1:y1:e", 6))
- return ERROR;
- if(!dht_memmem(buf, buflen, "1:y1:q", 6))
- return -1;
- if(dht_memmem(buf, buflen, "1:q4:ping", 9))
- return PING;
- if(dht_memmem(buf, buflen, "1:q9:find_node", 14))
- return FIND_NODE;
- if(dht_memmem(buf, buflen, "1:q9:get_peers", 14))
- return GET_PEERS;
- if(dht_memmem(buf, buflen, "1:q13:announce_peer", 19))
- return ANNOUNCE_PEER;
- return -1;
-
- overflow:
- debugf("Truncated message.\n");
- return -1;
-}
+++ /dev/null
-/*
-Copyright (c) 2009-2011 by Juliusz Chroboczek
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
-typedef void
-dht_callback(void *closure, int event,
- const unsigned char *info_hash,
- const void *data, size_t data_len);
-
-#define DHT_EVENT_NONE 0
-#define DHT_EVENT_VALUES 1
-#define DHT_EVENT_VALUES6 2
-#define DHT_EVENT_SEARCH_DONE 3
-#define DHT_EVENT_SEARCH_DONE6 4
-
-extern FILE *dht_debug;
-
-int dht_init(int s, int s6, const unsigned char *id, const unsigned char *v);
-int dht_insert_node(const unsigned char *id, struct sockaddr *sa, int salen);
-int dht_ping_node(struct sockaddr *sa, int salen);
-int dht_periodic(const void *buf, size_t buflen,
- const struct sockaddr *from, int fromlen,
- time_t *tosleep, dht_callback *callback, void *closure);
-int dht_search(const unsigned char *id, int port, int af,
- dht_callback *callback, void *closure);
-int dht_nodes(int af,
- int *good_return, int *dubious_return, int *cached_return,
- int *incoming_return);
-void dht_dump_tables(FILE *f);
-int dht_get_nodes(struct sockaddr_in *sin, int *num,
- struct sockaddr_in6 *sin6, int *num6);
-int dht_uninit(void);
-
-/* This must be provided by the user. */
-int dht_blacklisted(const struct sockaddr *sa, int salen);
-void dht_hash(void *hash_return, int hash_size,
- const void *v1, int len1,
- const void *v2, int len2,
- const void *v3, int len3);
-int dht_random_bytes(void *buf, size_t size);
+++ /dev/null
-libb64: Base64 Encoding/Decoding Routines
-======================================
-
-Authors:
--------
-
-Chris Venter chris.venter@gmail.com http://controlaltfire.com
-
-Contributors:
-------------
-
-Mario Rugiero
-Shlok Datye
-Peter K. Lee
-
+++ /dev/null
-libb64: Base64 Encoding/Decoding Routines
-======================================
-
-## Changelog ##
-
-Version 1.2.1 Release
----------------------
-Fixed a long-standing bug in src/cdecode.c where value_in was not correctly
-checked against the bounds [0..decoding_size)
-Thanks to both Mario Rugiero and Shlok Datye for pointing this out.
-Added some simple example code to answer some of the most common misconceptions
-people have about the library usage.
-
-Version 1.2 Release
--------------------
-Removed the b64dec, b64enc, encoder and decoder programs in favour of
-a better example, called base64, which encodes and decodes
-depending on its arguments.
-
-Created a solution for Microsoft Visual Studio C++ Express 2010
-edition, which simply builds the base64 example as a console application.
-
-Version 1.1 Release
--------------------
-Modified encode.h to (correctly) read from the iostream argument,
-instead of std::cin.
-Thanks to Peter K. Lee for the heads-up.
-
-No API changes.
-
-Version 1.0 Release
--------------------
-The current content is the changeset.
+++ /dev/null
-libb64: Base64 Encoding/Decoding Routines
-======================================
-
-Requirements:
-------------
-This piece of software has minimal requirements.
-
-I have tested it on the following systems:
-
-- a Linux machine, with the following specs:
-(this was the original development machine)
- * FedoraCore 4
- * kernel v. 2.6.11 (stock FC4 kernel)
- * gcc version 4.0.1 20050727 (Red Hat 4.0.1-5)
- * glibc-2.3.5-10
- * make v. 3.80
- * some arb version of makedepend
-
-- Windows XP machine
- * MSYS 1.0
- * MinGW 5.1.4
- * gcc version 3.4.5 (mingw-vista special r3)
-
-- Windows XP machine (same as above)
- * Microsoft Visual Studio 2010, Version 10.0.30319.1 RTMRel
-
-Barring any serious screwups on my part, this code should compile and run sweetly
-under Cygwin and other systems too. If you DO get it running under some weird arch/os setup,
-send me a mail, please.
-
-Compiling:
----------
-There is no configure. It would be overkill for something so simple...
-Run make in the root directory.
-
-Installing:
-----------
-Since the current targets are a standalone executable and a static library
-(fancy name for archive) with some headers, an install script has not been implemented yet.
-Simply copy the executable into your path, and use it.
-
---
-peace out
-Chris
+++ /dev/null
-Copyright-Only Dedication (based on United States law)
-or Public Domain Certification
-
-The person or persons who have associated work with this document (the
-"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
-his knowledge, the work of authorship identified is in the public domain of the
-country from which the work is published, or (b) hereby dedicates whatever
-copyright the dedicators holds in the work of authorship identified below (the
-"Work") to the public domain. A certifier, moreover, dedicates any copyright
-interest he may have in the associated work, and for these purposes, is
-described as a "dedicator" below.
-
-A certifier has taken reasonable steps to verify the copyright status of this
-work. Certifier recognizes that his good faith efforts may not shield him from
-liability if in fact the work certified is not in the public domain.
-
-Dedicator makes this dedication for the benefit of the public at large and to
-the detriment of the Dedicator's heirs and successors. Dedicator intends this
-dedication to be an overt act of relinquishment in perpetuity of all present
-and future rights under copyright law, whether vested or contingent, in the
-Work. Dedicator understands that such relinquishment of all rights includes
-the relinquishment of all rights to enforce (by lawsuit or otherwise) those
-copyrights in the Work.
-
-Dedicator recognizes that, once placed in the public domain, the Work may be
-freely reproduced, distributed, transmitted, used, modified, built upon, or
-otherwise exploited by anyone for any purpose, commercial or non-commercial,
-and in any way, including by methods that have not yet been invented or
-conceived.
\ No newline at end of file
+++ /dev/null
-noinst_LIBRARIES = libb64.a
-libb64_a_SOURCES = cdecode.c cencode.c
-noinst_HEADERS = b64/cdecode.h b64/cencode.h
-EXTRA_DIST = AUTHORS CHANGELOG INSTALL LICENSE README
+++ /dev/null
-b64: Base64 Encoding/Decoding Routines
-======================================
-
-Overview:
---------
-libb64 is a library of ANSI C routines for fast encoding/decoding data into and
-from a base64-encoded format. C++ wrappers are included, as well as the source
-code for standalone encoding and decoding executables.
-
-base64 consists of ASCII text, and is therefore a useful encoding for storing
-binary data in a text file, such as xml, or sending binary data over text-only
-email.
-
-References:
-----------
-* Wikipedia article:
- http://en.wikipedia.org/wiki/Base64
-* base64, another implementation of a commandline en/decoder:
- http://www.fourmilab.ch/webtools/base64/
-
-Why?
----
-I did this because I need an implementation of base64 encoding and decoding,
-without any licensing problems. Most OS implementations are released under
-either the GNU/GPL, or a BSD-variant, which is not what I require.
-
-Also, the chance to actually use the co-routine implementation in code is rare,
-and its use here is fitting. I couldn't pass up the chance.
-For more information on this technique, see "Coroutines in C", by Simon Tatham,
-which can be found online here:
-http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
-
-So then, under which license do I release this code? On to the next section...
-
-License:
--------
-This work is released under into the Public Domain.
-It basically boils down to this: I put this work in the public domain, and you
-can take it and do whatever you want with it.
-
-An example of this "license" is the Creative Commons Public Domain License, a
-copy of which can be found in the LICENSE file, and also online at
-http://creativecommons.org/licenses/publicdomain/
-
-Commandline Use:
----------------
-There is a new executable available, it is simply called base64.
-It can encode and decode files, as instructed by the user.
-
-To encode a file:
-$ ./base64 -e filea fileb
-fileb will now be the base64-encoded version of filea.
-
-To decode a file:
-$ ./base64 -d fileb filec
-filec will now be identical to filea.
-
-Programming:
------------
-Some C++ wrappers are provided as well, so you don't have to get your hands
-dirty. Encoding from standard input to standard output is as simple as
-
- #include <b64/encode.h>
- #include <iostream>
- int main()
- {
- base64::encoder E;
- E.encode(std::cin, std::cout);
- return 0;
- }
-
-Both standalone executables and a static library is provided in the package,
-
-Example code:
-------------
-The 'examples' directory contains some simple example C code, that demonstrates
-how to use the C interface of the library.
-
-Implementation:
---------------
-It is DAMN fast, if I may say so myself. The C code uses a little trick which
-has been used to implement coroutines, of which one can say that this
-implementation is an example.
-
-(To see how the libb64 codebase compares with some other BASE64 implementations
-available, see the BENCHMARKS file)
-
-The trick involves the fact that a switch-statement may legally cross into
-sub-blocks. A very thorough and enlightening essay on co-routines in C, using
-this method, can be found in the above mentioned "Coroutines in C", by Simon
-Tatham: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
-
-For example, an RLE decompressing routine, adapted from the article:
-1 static int STATE = 0;
-2 static int len, c;
-3 switch (STATE)
-4 {
-5 while (1)
-6 {
-7 c = getchar();
-8 if (c == EOF) return EOF;
-9 if (c == 0xFF) {
-10 len = getchar();
-11 c = getchar();
-12 while (len--)
-13 {
-14 STATE = 0;
-15 return c;
-16 case 0:
-17 }
-18 } else
-19 STATE = 1;
-20 return c;
-21 case 1:
-22 }
-23 }
-24 }
-
-As can be seen from this example, a coroutine depends on a state variable,
-which it sets directly before exiting (lines 14 and 119). The next time the
-routine is entered, the switch moves control to the specific point directly
-after the previous exit (lines 16 and 21).hands
-
-(As an aside, in the mentioned article the combination of the top-level switch,
-the various setting of the state, the return of a value, and the labelling of
-the exit point is wrapped in #define macros, making the structure of the
-routine even clearer.)
-
-The obvious problem with any such routine is the static keyword.
-Any static variables in a function spell doom for multithreaded applications.
-Also, in situations where this coroutine is used by more than one other
-coroutines, the consistency is disturbed.
-
-What is needed is a structure for storing these variabled, which is passed to
-the routine seperately. This obviously breaks the modularity of the function,
-since now the caller has to worry about and care for the internal state of the
-routine (the callee). This allows for a fast, multithreading-enabled
-implementation, which may (obviously) be wrapped in a C++ object for ease of
-use.
-
-The base64 encoding and decoding functionality in this package is implemented
-in exactly this way, providing both a high-speed high-maintanence C interface,
-and a wrapped C++ which is low-maintanence and only slightly less performant.
+++ /dev/null
-/*
-cdecode.h - c header for a base64 decoding algorithm
-
-This is part of the libb64 project, and has been placed in the public domain.
-For details, see http://sourceforge.net/projects/libb64
-*/
-
-#ifndef BASE64_CDECODE_H
-#define BASE64_CDECODE_H
-
-typedef enum
-{
- step_a, step_b, step_c, step_d
-} base64_decodestep;
-
-typedef struct
-{
- base64_decodestep step;
- char plainchar;
-} base64_decodestate;
-
-void base64_init_decodestate(base64_decodestate* state_in);
-
-int base64_decode_value(char value_in);
-
-int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
-
-#endif /* BASE64_CDECODE_H */
-
+++ /dev/null
-/*
-cencode.h - c header for a base64 encoding algorithm
-
-This is part of the libb64 project, and has been placed in the public domain.
-For details, see http://sourceforge.net/projects/libb64
-*/
-
-#ifndef BASE64_CENCODE_H
-#define BASE64_CENCODE_H
-
-typedef enum
-{
- step_A, step_B, step_C
-} base64_encodestep;
-
-typedef struct
-{
- base64_encodestep step;
- char result;
- int stepcount;
-} base64_encodestate;
-
-void base64_init_encodestate(base64_encodestate* state_in);
-
-char base64_encode_value(char value_in);
-
-int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
-
-int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
-
-#endif /* BASE64_CENCODE_H */
-
+++ /dev/null
-/*
-cdecoder.c - c source to a base64 decoding algorithm implementation
-
-This is part of the libb64 project, and has been placed in the public domain.
-For details, see http://sourceforge.net/projects/libb64
-*/
-
-#include <b64/cdecode.h>
-
-int base64_decode_value(char value_in)
-{
- static const signed char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
- static const char decoding_size = sizeof(decoding);
- value_in -= 43;
- if (value_in < 0 || value_in >= decoding_size) return -1;
- return decoding[(int)value_in];
-}
-
-void base64_init_decodestate(base64_decodestate* state_in)
-{
- state_in->step = step_a;
- state_in->plainchar = 0;
-}
-
-int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
-{
- const char* codechar = code_in;
- char* plainchar = plaintext_out;
- int fragment;
-
- *plainchar = state_in->plainchar;
-
- switch (state_in->step)
- {
- while (1)
- {
- case step_a:
- do {
- if (codechar == code_in+length_in)
- {
- state_in->step = step_a;
- state_in->plainchar = *plainchar;
- return plainchar - plaintext_out;
- }
- fragment = base64_decode_value(*codechar++);
- } while (fragment < 0);
- *plainchar = (fragment & 0x03f) << 2;
- case step_b:
- do {
- if (codechar == code_in+length_in)
- {
- state_in->step = step_b;
- state_in->plainchar = *plainchar;
- return plainchar - plaintext_out;
- }
- fragment = base64_decode_value(*codechar++);
- } while (fragment < 0);
- *plainchar++ |= (fragment & 0x030) >> 4;
- *plainchar = (fragment & 0x00f) << 4;
- case step_c:
- do {
- if (codechar == code_in+length_in)
- {
- state_in->step = step_c;
- state_in->plainchar = *plainchar;
- return plainchar - plaintext_out;
- }
- fragment = base64_decode_value(*codechar++);
- } while (fragment < 0);
- *plainchar++ |= (fragment & 0x03c) >> 2;
- *plainchar = (fragment & 0x003) << 6;
- case step_d:
- do {
- if (codechar == code_in+length_in)
- {
- state_in->step = step_d;
- state_in->plainchar = *plainchar;
- return plainchar - plaintext_out;
- }
- fragment = base64_decode_value(*codechar++);
- } while (fragment < 0);
- *plainchar++ |= (fragment & 0x03f);
- }
- }
- /* control should not reach here */
- return plainchar - plaintext_out;
-}
-
+++ /dev/null
-/*
-cencoder.c - c source to a base64 encoding algorithm implementation
-
-This is part of the libb64 project, and has been placed in the public domain.
-For details, see http://sourceforge.net/projects/libb64
-*/
-
-#include <b64/cencode.h>
-
-/*
-const int CHARS_PER_LINE = 72;
-*/
-
-void base64_init_encodestate(base64_encodestate* state_in)
-{
- state_in->step = step_A;
- state_in->result = 0;
- state_in->stepcount = 0;
-}
-
-char base64_encode_value(char value_in)
-{
- static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- if (value_in > 63) return '=';
- return encoding[(int)value_in];
-}
-
-int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
-{
- const char* plainchar = plaintext_in;
- const char* const plaintextend = plaintext_in + length_in;
- char* codechar = code_out;
- char result;
- char fragment;
-
- result = state_in->result;
-
- switch (state_in->step)
- {
- while (1)
- {
- case step_A:
- if (plainchar == plaintextend)
- {
- state_in->result = result;
- state_in->step = step_A;
- return codechar - code_out;
- }
- fragment = *plainchar++;
- result = (fragment & 0x0fc) >> 2;
- *codechar++ = base64_encode_value(result);
- result = (fragment & 0x003) << 4;
- case step_B:
- if (plainchar == plaintextend)
- {
- state_in->result = result;
- state_in->step = step_B;
- return codechar - code_out;
- }
- fragment = *plainchar++;
- result |= (fragment & 0x0f0) >> 4;
- *codechar++ = base64_encode_value(result);
- result = (fragment & 0x00f) << 2;
- case step_C:
- if (plainchar == plaintextend)
- {
- state_in->result = result;
- state_in->step = step_C;
- return codechar - code_out;
- }
- fragment = *plainchar++;
- result |= (fragment & 0x0c0) >> 6;
- *codechar++ = base64_encode_value(result);
- result = (fragment & 0x03f) >> 0;
- *codechar++ = base64_encode_value(result);
-
- /*
- ++(state_in->stepcount);
- if (state_in->stepcount == CHARS_PER_LINE/4)
- {
- *codechar++ = '\n';
- state_in->stepcount = 0;
- }
- */
- }
- }
- /* control should not reach here */
- return codechar - code_out;
-}
-
-int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
-{
- char* codechar = code_out;
-
- switch (state_in->step)
- {
- case step_B:
- *codechar++ = base64_encode_value(state_in->result);
- *codechar++ = '=';
- *codechar++ = '=';
- break;
- case step_C:
- *codechar++ = base64_encode_value(state_in->result);
- *codechar++ = '=';
- break;
- case step_A:
- break;
- }
- /*
- *codechar++ = '\n';
- */
-
- return codechar - code_out;
-}
-
+++ /dev/null
-$Id: Changelog.txt,v 1.29 2011/08/07 16:59:33 nanard Exp $
-
-2011/08/07:
- Patch to build on debian/kFreeBSD.
-
-2011/07/15:
- Put 3 clauses BSD licence at the top of source files.
-
-2011/06/18:
- --no-undefined => -Wl,--no-undefined
- adding a natpmpc.1 man page
-
-2011/05/19:
- Small fix in libnatpmpmodule.c thanks to Manuel Mausz
-
-2011/01/03:
- Added an argument to initnatpmp() in order to force the gateway to be used
-
-2011/01/01:
- fix in make install
-
-2010/05/21:
- make install now working under MacOSX (and BSD)
-
-2010/04/12:
- cplusplus stuff in natpmp.h
-
-2010/02/02:
- Fixed compilation under Mac OS X
-
-2009/12/19:
- improve and fix building under Windows.
- Project files for MS Visual Studio 2008
- More simple (and working) code for Win32.
- More checks in the /proc/net/route parsing. Add some comments.
-
-2009/08/04:
- improving getgateway.c for windows
-
-2009/07/13:
- Adding Haiku code in getgateway.c
-
-2009/06/04:
- Adding Python module thanks to David Wu
-
-2009/03/10:
- Trying to have windows get gateway working if not using DHCP
-
-2009/02/27:
- dont include declspec.h if not under WIN32.
-
-2009/01/23:
- Prefixed the libraries name with lib
-
-2008/10/06:
- Fixed a memory leak in getdefaultgateway() (USE_SYSCTL_NET_ROUTE)
-
-2008/07/03:
- Adding WIN32 code from Robbie Hanson
-
-2008/06/30:
- added a Solaris implementation for getgateway().
- added a LICENCE file to the distribution
-
-2008/05/29:
- Anonymous unions are forbidden in ANSI C. That was causing problems with
- non-GCC compilers.
-
-2008/04/28:
- introduced strnatpmperr()
- improved natpmpc.c sample
- make install now install the binary
-
-2007/12/13:
- Fixed getgateway.c for working under OS X ;)
- Fixed values for NATPMP_PROTOCOL_TCP and NATPMP_PROTOCOL_UDP
-
-2007/12/11:
- Fixed getgateway.c for compilation under Mac OS X
-
-2007/12/01:
- added some comments in .h
-
-2007/11/30:
- implemented almost everything
-
+++ /dev/null
-Copyright (c) 2007-2011, Thomas BERNARD
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
+++ /dev/null
-noinst_LIBRARIES = libnatpmp.a
-
-AM_CFLAGS = @PTHREAD_CFLAGS@ -DENABLE_STRNATPMPERR
-
-libnatpmp_a_SOURCES = \
- getgateway.c \
- natpmp.c \
- wingettimeofday.c
-
-noinst_HEADERS = \
- declspec.h \
- getgateway.h \
- natpmp.h \
- wingettimeofday.h
-
-EXTRA_DIST = \
- README \
- LICENSE
+++ /dev/null
-libnatpmp (c) 2007-2009 Thomas Bernard
-contact : miniupnp@free.fr
-
-see http://miniupnp.free.fr/libnatpmp.html
-or http://miniupnp.tuxfamily.org/libnatpmp.html
-for some documentation and code samples.
-
+++ /dev/null
-#ifndef __DECLSPEC_H__
-#define __DECLSPEC_H__
-
-#if defined(WIN32) && !defined(STATICLIB)
- #ifdef NATPMP_EXPORTS
- #define LIBSPEC __declspec(dllexport)
- #else
- #define LIBSPEC __declspec(dllimport)
- #endif
-#else
- #define LIBSPEC
-#endif
-
-#endif
-
+++ /dev/null
-/* $Id: getgateway.c,v 1.22 2011/08/08 21:20:51 nanard Exp $ */
-/* libnatpmp
-
-Copyright (c) 2007-2011, Thomas BERNARD
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-*/
-#include <stdio.h>
-#include <ctype.h>
-#ifndef WIN32
-#include <netinet/in.h>
-#endif
-#if !defined(_MSC_VER)
-#include <sys/param.h>
-#endif
-/* There is no portable method to get the default route gateway.
- * So below are four (or five ?) differents functions implementing this.
- * Parsing /proc/net/route is for linux.
- * sysctl is the way to access such informations on BSD systems.
- * Many systems should provide route information through raw PF_ROUTE
- * sockets.
- * In MS Windows, default gateway is found by looking into the registry
- * or by using GetBestRoute(). */
-#ifdef __linux__
-#define USE_PROC_NET_ROUTE
-#undef USE_SOCKET_ROUTE
-#undef USE_SYSCTL_NET_ROUTE
-#endif
-
-#if defined(BSD) || defined(__FreeBSD_kernel__)
-#undef USE_PROC_NET_ROUTE
-#define USE_SOCKET_ROUTE
-#undef USE_SYSCTL_NET_ROUTE
-#endif
-
-#ifdef __APPLE__
-#undef USE_PROC_NET_ROUTE
-#undef USE_SOCKET_ROUTE
-#define USE_SYSCTL_NET_ROUTE
-#endif
-
-#if (defined(sun) && defined(__SVR4))
-#undef USE_PROC_NET_ROUTE
-#define USE_SOCKET_ROUTE
-#undef USE_SYSCTL_NET_ROUTE
-#endif
-
-#ifdef WIN32
-#undef USE_PROC_NET_ROUTE
-#undef USE_SOCKET_ROUTE
-#undef USE_SYSCTL_NET_ROUTE
-//#define USE_WIN32_CODE
-#define USE_WIN32_CODE_2
-#endif
-
-#ifdef __CYGWIN__
-#undef USE_PROC_NET_ROUTE
-#undef USE_SOCKET_ROUTE
-#undef USE_SYSCTL_NET_ROUTE
-#define USE_WIN32_CODE
-#include <stdarg.h>
-#include <w32api/windef.h>
-#include <w32api/winbase.h>
-#include <w32api/winreg.h>
-#endif
-
-#ifdef __HAIKU__
-#include <stdlib.h>
-#include <unistd.h>
-#include <net/if.h>
-#include <sys/sockio.h>
-#define USE_HAIKU_CODE
-#endif
-
-#ifdef USE_SYSCTL_NET_ROUTE
-#include <stdlib.h>
-#include <sys/sysctl.h>
-#include <sys/socket.h>
-#include <net/route.h>
-#endif
-#ifdef USE_SOCKET_ROUTE
-#include <unistd.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <net/if.h>
-#include <net/route.h>
-#endif
-
-#ifdef USE_WIN32_CODE
-#include <unknwn.h>
-#include <winreg.h>
-#define MAX_KEY_LENGTH 255
-#define MAX_VALUE_LENGTH 16383
-#endif
-
-#ifdef USE_WIN32_CODE_2
-#include <windows.h>
-#include <iphlpapi.h>
-#endif
-
-#include "getgateway.h"
-
-#ifndef WIN32
-#define SUCCESS (0)
-#define FAILED (-1)
-#endif
-
-#ifdef USE_PROC_NET_ROUTE
-/*
- parse /proc/net/route which is as follow :
-
-Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
-wlan0 0001A8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
-eth0 0000FEA9 00000000 0001 0 0 0 0000FFFF 0 0 0
-wlan0 00000000 0101A8C0 0003 0 0 0 00000000 0 0 0
-eth0 00000000 00000000 0001 0 0 1000 00000000 0 0 0
-
- One header line, and then one line by route by route table entry.
-*/
-int getdefaultgateway(in_addr_t * addr)
-{
- unsigned long d, g;
- char buf[256];
- int line = 0;
- FILE * f;
- char * p;
- f = fopen("/proc/net/route", "r");
- if(!f)
- return FAILED;
- while(fgets(buf, sizeof(buf), f)) {
- if(line > 0) { /* skip the first line */
- p = buf;
- /* skip the interface name */
- while(*p && !isspace(*p))
- p++;
- while(*p && isspace(*p))
- p++;
- if(sscanf(p, "%lx%lx", &d, &g)==2) {
- if(d == 0 && g != 0) { /* default */
- *addr = g;
- fclose(f);
- return SUCCESS;
- }
- }
- }
- line++;
- }
- /* default route not found ! */
- if(f)
- fclose(f);
- return FAILED;
-}
-#endif /* #ifdef USE_PROC_NET_ROUTE */
-
-
-#ifdef USE_SYSCTL_NET_ROUTE
-
-#define ROUNDUP(a) \
- ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
-
-int getdefaultgateway(in_addr_t * addr)
-{
-#if 0
- /* net.route.0.inet.dump.0.0 ? */
- int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
- NET_RT_DUMP, 0, 0/*tableid*/};
-#endif
- /* net.route.0.inet.flags.gateway */
- int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET,
- NET_RT_FLAGS, RTF_GATEWAY};
- size_t l;
- char * buf, * p;
- struct rt_msghdr * rt;
- struct sockaddr * sa;
- struct sockaddr * sa_tab[RTAX_MAX];
- int i;
- int r = FAILED;
- if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) {
- return FAILED;
- }
- if(l>0) {
- buf = malloc(l);
- if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) {
- free(buf);
- return FAILED;
- }
- for(p=buf; p<buf+l; p+=rt->rtm_msglen) {
- rt = (struct rt_msghdr *)p;
- sa = (struct sockaddr *)(rt + 1);
- for(i=0; i<RTAX_MAX; i++) {
- if(rt->rtm_addrs & (1 << i)) {
- sa_tab[i] = sa;
- sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len));
- } else {
- sa_tab[i] = NULL;
- }
- }
- if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
- && sa_tab[RTAX_DST]->sa_family == AF_INET
- && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) {
- if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) {
- *addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr;
- r = SUCCESS;
- }
- }
- }
- free(buf);
- }
- return r;
-}
-#endif /* #ifdef USE_SYSCTL_NET_ROUTE */
-
-
-#ifdef USE_SOCKET_ROUTE
-/* Thanks to Darren Kenny for this code */
-#define NEXTADDR(w, u) \
- if (rtm_addrs & (w)) {\
- l = sizeof(struct sockaddr); memmove(cp, &(u), l); cp += l;\
- }
-
-#define rtm m_rtmsg.m_rtm
-
-struct {
- struct rt_msghdr m_rtm;
- char m_space[512];
-} m_rtmsg;
-
-int getdefaultgateway(in_addr_t *addr)
-{
- int s, seq, l, rtm_addrs, i;
- pid_t pid;
- struct sockaddr so_dst, so_mask;
- char *cp = m_rtmsg.m_space;
- struct sockaddr *gate = NULL, *sa;
- struct rt_msghdr *msg_hdr;
-
- pid = getpid();
- seq = 0;
- rtm_addrs = RTA_DST | RTA_NETMASK;
-
- memset(&so_dst, 0, sizeof(so_dst));
- memset(&so_mask, 0, sizeof(so_mask));
- memset(&rtm, 0, sizeof(struct rt_msghdr));
-
- rtm.rtm_type = RTM_GET;
- rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
- rtm.rtm_version = RTM_VERSION;
- rtm.rtm_seq = ++seq;
- rtm.rtm_addrs = rtm_addrs;
-
- so_dst.sa_family = AF_INET;
- so_mask.sa_family = AF_INET;
-
- NEXTADDR(RTA_DST, so_dst);
- NEXTADDR(RTA_NETMASK, so_mask);
-
- rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
-
- s = socket(PF_ROUTE, SOCK_RAW, 0);
-
- if (write(s, (char *)&m_rtmsg, l) < 0) {
- close(s);
- return FAILED;
- }
-
- do {
- l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
- } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
-
- close(s);
-
- msg_hdr = &rtm;
-
- cp = ((char *)(msg_hdr + 1));
- if (msg_hdr->rtm_addrs) {
- for (i = 1; i; i <<= 1)
- if (i & msg_hdr->rtm_addrs) {
- sa = (struct sockaddr *)cp;
- if (i == RTA_GATEWAY )
- gate = sa;
-
- cp += sizeof(struct sockaddr);
- }
- } else {
- return FAILED;
- }
-
-
- if (gate != NULL ) {
- *addr = ((struct sockaddr_in *)gate)->sin_addr.s_addr;
- return SUCCESS;
- } else {
- return FAILED;
- }
-}
-#endif /* #ifdef USE_SOCKET_ROUTE */
-
-#ifdef USE_WIN32_CODE
-LIBSPEC int getdefaultgateway(in_addr_t * addr)
-{
- HKEY networkCardsKey;
- HKEY networkCardKey;
- HKEY interfacesKey;
- HKEY interfaceKey;
- DWORD i = 0;
- DWORD numSubKeys = 0;
- TCHAR keyName[MAX_KEY_LENGTH];
- DWORD keyNameLength = MAX_KEY_LENGTH;
- TCHAR keyValue[MAX_VALUE_LENGTH];
- DWORD keyValueLength = MAX_VALUE_LENGTH;
- DWORD keyValueType = REG_SZ;
- TCHAR gatewayValue[MAX_VALUE_LENGTH];
- DWORD gatewayValueLength = MAX_VALUE_LENGTH;
- DWORD gatewayValueType = REG_MULTI_SZ;
- int done = 0;
-
- //const char * networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
- //const char * interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
-#ifdef UNICODE
- LPCTSTR networkCardsPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
- LPCTSTR interfacesPath = L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
-#define STR_SERVICENAME L"ServiceName"
-#define STR_DHCPDEFAULTGATEWAY L"DhcpDefaultGateway"
-#define STR_DEFAULTGATEWAY L"DefaultGateway"
-#else
- LPCTSTR networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
- LPCTSTR interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
-#define STR_SERVICENAME "ServiceName"
-#define STR_DHCPDEFAULTGATEWAY "DhcpDefaultGateway"
-#define STR_DEFAULTGATEWAY "DefaultGateway"
-#endif
- // The windows registry lists its primary network devices in the following location:
- // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards
- //
- // Each network device has its own subfolder, named with an index, with various properties:
- // -NetworkCards
- // -5
- // -Description = Broadcom 802.11n Network Adapter
- // -ServiceName = {E35A72F8-5065-4097-8DFE-C7790774EE4D}
- // -8
- // -Description = Marvell Yukon 88E8058 PCI-E Gigabit Ethernet Controller
- // -ServiceName = {86226414-5545-4335-A9D1-5BD7120119AD}
- //
- // The above service name is the name of a subfolder within:
- // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces
- //
- // There may be more subfolders in this interfaces path than listed in the network cards path above:
- // -Interfaces
- // -{3a539854-6a70-11db-887c-806e6f6e6963}
- // -DhcpIPAddress = 0.0.0.0
- // -[more]
- // -{E35A72F8-5065-4097-8DFE-C7790774EE4D}
- // -DhcpIPAddress = 10.0.1.4
- // -DhcpDefaultGateway = 10.0.1.1
- // -[more]
- // -{86226414-5545-4335-A9D1-5BD7120119AD}
- // -DhcpIpAddress = 10.0.1.5
- // -DhcpDefaultGateay = 10.0.1.1
- // -[more]
- //
- // In order to extract this information, we enumerate each network card, and extract the ServiceName value.
- // This is then used to open the interface subfolder, and attempt to extract a DhcpDefaultGateway value.
- // Once one is found, we're done.
- //
- // It may be possible to simply enumerate the interface folders until we find one with a DhcpDefaultGateway value.
- // However, the technique used is the technique most cited on the web, and we assume it to be more correct.
-
- if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predifined key
- networkCardsPath, // Name of registry subkey to open
- 0, // Reserved - must be zero
- KEY_READ, // Mask - desired access rights
- &networkCardsKey)) // Pointer to output key
- {
- // Unable to open network cards keys
- return -1;
- }
-
- if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predefined key
- interfacesPath, // Name of registry subkey to open
- 0, // Reserved - must be zero
- KEY_READ, // Mask - desired access rights
- &interfacesKey)) // Pointer to output key
- {
- // Unable to open interfaces key
- RegCloseKey(networkCardsKey);
- return -1;
- }
-
- // Figure out how many subfolders are within the NetworkCards folder
- RegQueryInfoKey(networkCardsKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
-
- //printf( "Number of subkeys: %u\n", (unsigned int)numSubKeys);
-
- // Enumrate through each subfolder within the NetworkCards folder
- for(i = 0; i < numSubKeys && !done; i++)
- {
- keyNameLength = MAX_KEY_LENGTH;
- if(ERROR_SUCCESS == RegEnumKeyEx(networkCardsKey, // Open registry key
- i, // Index of subkey to retrieve
- keyName, // Buffer that receives the name of the subkey
- &keyNameLength, // Variable that receives the size of the above buffer
- NULL, // Reserved - must be NULL
- NULL, // Buffer that receives the class string
- NULL, // Variable that receives the size of the above buffer
- NULL)) // Variable that receives the last write time of subkey
- {
- if(RegOpenKeyEx(networkCardsKey, keyName, 0, KEY_READ, &networkCardKey) == ERROR_SUCCESS)
- {
- keyValueLength = MAX_VALUE_LENGTH;
- if(ERROR_SUCCESS == RegQueryValueEx(networkCardKey, // Open registry key
- STR_SERVICENAME, // Name of key to query
- NULL, // Reserved - must be NULL
- &keyValueType, // Receives value type
- (LPBYTE)keyValue, // Receives value
- &keyValueLength)) // Receives value length in bytes
- {
-// printf("keyValue: %s\n", keyValue);
- if(RegOpenKeyEx(interfacesKey, keyValue, 0, KEY_READ, &interfaceKey) == ERROR_SUCCESS)
- {
- gatewayValueLength = MAX_VALUE_LENGTH;
- if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey, // Open registry key
- STR_DHCPDEFAULTGATEWAY, // Name of key to query
- NULL, // Reserved - must be NULL
- &gatewayValueType, // Receives value type
- (LPBYTE)gatewayValue, // Receives value
- &gatewayValueLength)) // Receives value length in bytes
- {
- // Check to make sure it's a string
- if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1))
- {
- //printf("gatewayValue: %s\n", gatewayValue);
- done = 1;
- }
- }
- else if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey, // Open registry key
- STR_DEFAULTGATEWAY, // Name of key to query
- NULL, // Reserved - must be NULL
- &gatewayValueType, // Receives value type
- (LPBYTE)gatewayValue,// Receives value
- &gatewayValueLength)) // Receives value length in bytes
- {
- // Check to make sure it's a string
- if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1))
- {
- //printf("gatewayValue: %s\n", gatewayValue);
- done = 1;
- }
- }
- RegCloseKey(interfaceKey);
- }
- }
- RegCloseKey(networkCardKey);
- }
- }
- }
-
- RegCloseKey(interfacesKey);
- RegCloseKey(networkCardsKey);
-
- if(done)
- {
-#if UNICODE
- char tmp[32];
- for(i = 0; i < 32; i++) {
- tmp[i] = (char)gatewayValue[i];
- if(!tmp[i])
- break;
- }
- tmp[31] = '\0';
- *addr = inet_addr(tmp);
-#else
- *addr = inet_addr(gatewayValue);
-#endif
- return 0;
- }
-
- return -1;
-}
-#endif /* #ifdef USE_WIN32_CODE */
-
-#ifdef USE_WIN32_CODE_2
-int getdefaultgateway(in_addr_t *addr)
-{
- MIB_IPFORWARDROW ip_forward;
- memset(&ip_forward, 0, sizeof(ip_forward));
- if(GetBestRoute(inet_addr("0.0.0.0"), 0, &ip_forward) != NO_ERROR)
- return -1;
- *addr = ip_forward.dwForwardNextHop;
- return 0;
-}
-#endif /* #ifdef USE_WIN32_CODE_2 */
-
-#ifdef USE_HAIKU_CODE
-int getdefaultgateway(in_addr_t *addr)
-{
- int fd, ret = -1;
- struct ifconf config;
- void *buffer = NULL;
- struct ifreq *interface;
-
- if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- return -1;
- }
- if (ioctl(fd, SIOCGRTSIZE, &config, sizeof(config)) != 0) {
- goto fail;
- }
- if (config.ifc_value < 1) {
- goto fail; /* No routes */
- }
- if ((buffer = malloc(config.ifc_value)) == NULL) {
- goto fail;
- }
- config.ifc_len = config.ifc_value;
- config.ifc_buf = buffer;
- if (ioctl(fd, SIOCGRTTABLE, &config, sizeof(config)) != 0) {
- goto fail;
- }
- for (interface = buffer;
- (uint8_t *)interface < (uint8_t *)buffer + config.ifc_len; ) {
- struct route_entry route = interface->ifr_route;
- int intfSize;
- if (route.flags & (RTF_GATEWAY | RTF_DEFAULT)) {
- *addr = ((struct sockaddr_in *)route.gateway)->sin_addr.s_addr;
- ret = 0;
- break;
- }
- intfSize = sizeof(route) + IF_NAMESIZE;
- if (route.destination != NULL) {
- intfSize += route.destination->sa_len;
- }
- if (route.mask != NULL) {
- intfSize += route.mask->sa_len;
- }
- if (route.gateway != NULL) {
- intfSize += route.gateway->sa_len;
- }
- interface = (struct ifreq *)((uint8_t *)interface + intfSize);
- }
-fail:
- free(buffer);
- close(fd);
- return ret;
-}
-#endif /* #ifdef USE_HAIKU_CODE */
-
-
+++ /dev/null
-/* $Id: getgateway.h,v 1.5 2011/07/15 08:30:11 nanard Exp $ */
-/* libnatpmp
-Copyright (c) 2007-2011, Thomas BERNARD
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-*/
-#ifndef __GETGATEWAY_H__
-#define __GETGATEWAY_H__
-
-#ifdef WIN32
-#if !defined(_MSC_VER)
-#include <stdint.h>
-#else
-typedef unsigned long uint32_t;
-typedef unsigned short uint16_t;
-#endif
-#define in_addr_t uint32_t
-#endif
-#include "declspec.h"
-
-/* getdefaultgateway() :
- * return value :
- * 0 : success
- * -1 : failure */
-LIBSPEC int getdefaultgateway(in_addr_t * addr);
-
-#endif
+++ /dev/null
-/* $Id: natpmp.c,v 1.14 2011/07/15 08:30:11 nanard Exp $ */
-/* libnatpmp
-Copyright (c) 2007-2011, Thomas BERNARD
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-*/
-#ifdef __linux__
-#define _BSD_SOURCE 1
-#endif
-#include <string.h>
-#include <time.h>
-#if !defined(_MSC_VER)
-#include <sys/time.h>
-#endif
-#ifdef WIN32
-#include <errno.h>
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <io.h>
-#define EWOULDBLOCK WSAEWOULDBLOCK
-#define ECONNREFUSED WSAECONNREFUSED
-#include "wingettimeofday.h"
-#else
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#define closesocket close
-#endif
-#include "natpmp.h"
-#include "getgateway.h"
-
-LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw)
-{
-#ifdef WIN32
- u_long ioctlArg = 1;
-#else
- int flags;
-#endif
- struct sockaddr_in addr;
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- memset(p, 0, sizeof(natpmp_t));
- p->s = socket(PF_INET, SOCK_DGRAM, 0);
- if(p->s < 0)
- return NATPMP_ERR_SOCKETERROR;
-#ifdef WIN32
- if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR)
- return NATPMP_ERR_FCNTLERROR;
-#else
- if((flags = fcntl(p->s, F_GETFL, 0)) < 0)
- return NATPMP_ERR_FCNTLERROR;
- if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0)
- return NATPMP_ERR_FCNTLERROR;
-#endif
-
- if(forcegw) {
- p->gateway = forcedgw;
- } else {
- if(getdefaultgateway(&(p->gateway)) < 0)
- return NATPMP_ERR_CANNOTGETGATEWAY;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(NATPMP_PORT);
- addr.sin_addr.s_addr = p->gateway;
- if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
- return NATPMP_ERR_CONNECTERR;
- return 0;
-}
-
-LIBSPEC int closenatpmp(natpmp_t * p)
-{
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- if(closesocket(p->s) < 0)
- return NATPMP_ERR_CLOSEERR;
- return 0;
-}
-
-int sendpendingrequest(natpmp_t * p)
-{
- int r;
-/* struct sockaddr_in addr;*/
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
-/* memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(NATPMP_PORT);
- addr.sin_addr.s_addr = p->gateway;
- r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
- (struct sockaddr *)&addr, sizeof(addr));*/
- r = (int)send(p->s, p->pending_request, p->pending_request_len, 0);
- return (r<0) ? NATPMP_ERR_SENDERR : r;
-}
-
-int sendnatpmprequest(natpmp_t * p)
-{
- int n;
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- /* TODO : check if no request is allready pending */
- p->has_pending_request = 1;
- p->try_number = 1;
- n = sendpendingrequest(p);
- gettimeofday(&p->retry_time, NULL); // check errors !
- p->retry_time.tv_usec += 250000; /* add 250ms */
- if(p->retry_time.tv_usec >= 1000000) {
- p->retry_time.tv_usec -= 1000000;
- p->retry_time.tv_sec++;
- }
- return n;
-}
-
-LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout)
-{
- struct timeval now;
- if(!p || !timeout)
- return NATPMP_ERR_INVALIDARGS;
- if(!p->has_pending_request)
- return NATPMP_ERR_NOPENDINGREQ;
- if(gettimeofday(&now, NULL) < 0)
- return NATPMP_ERR_GETTIMEOFDAYERR;
- timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
- timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
- if(timeout->tv_usec < 0) {
- timeout->tv_usec += 1000000;
- timeout->tv_sec--;
- }
- return 0;
-}
-
-LIBSPEC int sendpublicaddressrequest(natpmp_t * p)
-{
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- //static const unsigned char request[] = { 0, 0 };
- p->pending_request[0] = 0;
- p->pending_request[1] = 0;
- p->pending_request_len = 2;
- // TODO: return 0 instead of sizeof(request) ??
- return sendnatpmprequest(p);
-}
-
-LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol,
- uint16_t privateport, uint16_t publicport,
- uint32_t lifetime)
-{
- if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP))
- return NATPMP_ERR_INVALIDARGS;
- p->pending_request[0] = 0;
- p->pending_request[1] = protocol;
- p->pending_request[2] = 0;
- p->pending_request[3] = 0;
- *((uint16_t *)(p->pending_request + 4)) = htons(privateport);
- *((uint16_t *)(p->pending_request + 6)) = htons(publicport);
- *((uint32_t *)(p->pending_request + 8)) = htonl(lifetime);
- p->pending_request_len = 12;
- return sendnatpmprequest(p);
-}
-
-LIBSPEC int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response)
-{
- unsigned char buf[16];
- struct sockaddr_in addr;
- socklen_t addrlen = sizeof(addr);
- int n;
- if(!p)
- return NATPMP_ERR_INVALIDARGS;
- n = recvfrom(p->s, buf, sizeof(buf), 0,
- (struct sockaddr *)&addr, &addrlen);
- if(n<0)
- switch(errno) {
- /*case EAGAIN:*/
- case EWOULDBLOCK:
- n = NATPMP_TRYAGAIN;
- break;
- case ECONNREFUSED:
- n = NATPMP_ERR_NOGATEWAYSUPPORT;
- break;
- default:
- n = NATPMP_ERR_RECVFROM;
- }
- /* check that addr is correct (= gateway) */
- else if(addr.sin_addr.s_addr != p->gateway)
- n = NATPMP_ERR_WRONGPACKETSOURCE;
- else {
- response->resultcode = ntohs(*((uint16_t *)(buf + 2)));
- response->epoch = ntohl(*((uint32_t *)(buf + 4)));
- if(buf[0] != 0)
- n = NATPMP_ERR_UNSUPPORTEDVERSION;
- else if(buf[1] < 128 || buf[1] > 130)
- n = NATPMP_ERR_UNSUPPORTEDOPCODE;
- else if(response->resultcode != 0) {
- switch(response->resultcode) {
- case 1:
- n = NATPMP_ERR_UNSUPPORTEDVERSION;
- break;
- case 2:
- n = NATPMP_ERR_NOTAUTHORIZED;
- break;
- case 3:
- n = NATPMP_ERR_NETWORKFAILURE;
- break;
- case 4:
- n = NATPMP_ERR_OUTOFRESOURCES;
- break;
- case 5:
- n = NATPMP_ERR_UNSUPPORTEDOPCODE;
- break;
- default:
- n = NATPMP_ERR_UNDEFINEDERROR;
- }
- } else {
- response->type = buf[1] & 0x7f;
- if(buf[1] == 128)
- //response->publicaddress.addr = *((uint32_t *)(buf + 8));
- response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8));
- else {
- response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8)));
- response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10)));
- response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12)));
- }
- n = 0;
- }
- }
- return n;
-}
-
-int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response)
-{
- int n;
- if(!p || !response)
- return NATPMP_ERR_INVALIDARGS;
- if(!p->has_pending_request)
- return NATPMP_ERR_NOPENDINGREQ;
- n = readnatpmpresponse(p, response);
- if(n<0) {
- if(n==NATPMP_TRYAGAIN) {
- struct timeval now;
- gettimeofday(&now, NULL); // check errors !
- if(timercmp(&now, &p->retry_time, >=)) {
- int delay, r;
- if(p->try_number >= 9) {
- return NATPMP_ERR_NOGATEWAYSUPPORT;
- }
- /*printf("retry! %d\n", p->try_number);*/
- delay = 250 * (1<<p->try_number); // ms
- /*for(i=0; i<p->try_number; i++)
- delay += delay;*/
- p->retry_time.tv_sec += (delay / 1000);
- p->retry_time.tv_usec += (delay % 1000) * 1000;
- if(p->retry_time.tv_usec >= 1000000) {
- p->retry_time.tv_usec -= 1000000;
- p->retry_time.tv_sec++;
- }
- p->try_number++;
- r = sendpendingrequest(p);
- if(r<0)
- return r;
- }
- }
- } else {
- p->has_pending_request = 0;
- }
- return n;
-}
-
-#ifdef ENABLE_STRNATPMPERR
-LIBSPEC const char * strnatpmperr(int r)
-{
- const char * s;
- switch(r) {
- case NATPMP_ERR_INVALIDARGS:
- s = "invalid arguments";
- break;
- case NATPMP_ERR_SOCKETERROR:
- s = "socket() failed";
- break;
- case NATPMP_ERR_CANNOTGETGATEWAY:
- s = "cannot get default gateway ip address";
- break;
- case NATPMP_ERR_CLOSEERR:
-#ifdef WIN32
- s = "closesocket() failed";
-#else
- s = "close() failed";
-#endif
- break;
- case NATPMP_ERR_RECVFROM:
- s = "recvfrom() failed";
- break;
- case NATPMP_ERR_NOPENDINGREQ:
- s = "no pending request";
- break;
- case NATPMP_ERR_NOGATEWAYSUPPORT:
- s = "the gateway does not support nat-pmp";
- break;
- case NATPMP_ERR_CONNECTERR:
- s = "connect() failed";
- break;
- case NATPMP_ERR_WRONGPACKETSOURCE:
- s = "packet not received from the default gateway";
- break;
- case NATPMP_ERR_SENDERR:
- s = "send() failed";
- break;
- case NATPMP_ERR_FCNTLERROR:
- s = "fcntl() failed";
- break;
- case NATPMP_ERR_GETTIMEOFDAYERR:
- s = "gettimeofday() failed";
- break;
- case NATPMP_ERR_UNSUPPORTEDVERSION:
- s = "unsupported nat-pmp version error from server";
- break;
- case NATPMP_ERR_UNSUPPORTEDOPCODE:
- s = "unsupported nat-pmp opcode error from server";
- break;
- case NATPMP_ERR_UNDEFINEDERROR:
- s = "undefined nat-pmp server error";
- break;
- case NATPMP_ERR_NOTAUTHORIZED:
- s = "not authorized";
- break;
- case NATPMP_ERR_NETWORKFAILURE:
- s = "network failure";
- break;
- case NATPMP_ERR_OUTOFRESOURCES:
- s = "nat-pmp server out of resources";
- break;
- default:
- s = "Unknown libnatpmp error";
- }
- return s;
-}
-#endif
-
+++ /dev/null
-/* $Id: natpmp.h,v 1.15 2011/07/15 08:30:11 nanard Exp $ */
-/* libnatpmp
-Copyright (c) 2007-2011, Thomas BERNARD
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-*/
-#ifndef __NATPMP_H__
-#define __NATPMP_H__
-
-/* NAT-PMP Port as defined by the NAT-PMP draft */
-#define NATPMP_PORT (5351)
-
-#include <time.h>
-#if !defined(_MSC_VER)
-#include <sys/time.h>
-#endif
-#ifdef WIN32
-#include <winsock2.h>
-#if !defined(_MSC_VER)
-#include <stdint.h>
-#else
-typedef unsigned long uint32_t;
-typedef unsigned short uint16_t;
-#endif
-#define in_addr_t uint32_t
-#include "declspec.h"
-#else
-#define LIBSPEC
-#include <netinet/in.h>
-#endif
-
-typedef struct {
- int s; /* socket */
- in_addr_t gateway; /* default gateway (IPv4) */
- int has_pending_request;
- unsigned char pending_request[12];
- int pending_request_len;
- int try_number;
- struct timeval retry_time;
-} natpmp_t;
-
-typedef struct {
- uint16_t type; /* NATPMP_RESPTYPE_* */
- uint16_t resultcode; /* NAT-PMP response code */
- uint32_t epoch; /* Seconds since start of epoch */
- union {
- struct {
- //in_addr_t addr;
- struct in_addr addr;
- } publicaddress;
- struct {
- uint16_t privateport;
- uint16_t mappedpublicport;
- uint32_t lifetime;
- } newportmapping;
- } pnu;
-} natpmpresp_t;
-
-/* possible values for type field of natpmpresp_t */
-#define NATPMP_RESPTYPE_PUBLICADDRESS (0)
-#define NATPMP_RESPTYPE_UDPPORTMAPPING (1)
-#define NATPMP_RESPTYPE_TCPPORTMAPPING (2)
-
-/* Values to pass to sendnewportmappingrequest() */
-#define NATPMP_PROTOCOL_UDP (1)
-#define NATPMP_PROTOCOL_TCP (2)
-
-/* return values */
-/* NATPMP_ERR_INVALIDARGS : invalid arguments passed to the function */
-#define NATPMP_ERR_INVALIDARGS (-1)
-/* NATPMP_ERR_SOCKETERROR : socket() failed. check errno for details */
-#define NATPMP_ERR_SOCKETERROR (-2)
-/* NATPMP_ERR_CANNOTGETGATEWAY : can't get default gateway IP */
-#define NATPMP_ERR_CANNOTGETGATEWAY (-3)
-/* NATPMP_ERR_CLOSEERR : close() failed. check errno for details */
-#define NATPMP_ERR_CLOSEERR (-4)
-/* NATPMP_ERR_RECVFROM : recvfrom() failed. check errno for details */
-#define NATPMP_ERR_RECVFROM (-5)
-/* NATPMP_ERR_NOPENDINGREQ : readnatpmpresponseorretry() called while
- * no NAT-PMP request was pending */
-#define NATPMP_ERR_NOPENDINGREQ (-6)
-/* NATPMP_ERR_NOGATEWAYSUPPORT : the gateway does not support NAT-PMP */
-#define NATPMP_ERR_NOGATEWAYSUPPORT (-7)
-/* NATPMP_ERR_CONNECTERR : connect() failed. check errno for details */
-#define NATPMP_ERR_CONNECTERR (-8)
-/* NATPMP_ERR_WRONGPACKETSOURCE : packet not received from the network gateway */
-#define NATPMP_ERR_WRONGPACKETSOURCE (-9)
-/* NATPMP_ERR_SENDERR : send() failed. check errno for details */
-#define NATPMP_ERR_SENDERR (-10)
-/* NATPMP_ERR_FCNTLERROR : fcntl() failed. check errno for details */
-#define NATPMP_ERR_FCNTLERROR (-11)
-/* NATPMP_ERR_GETTIMEOFDAYERR : gettimeofday() failed. check errno for details */
-#define NATPMP_ERR_GETTIMEOFDAYERR (-12)
-
-/* */
-#define NATPMP_ERR_UNSUPPORTEDVERSION (-14)
-#define NATPMP_ERR_UNSUPPORTEDOPCODE (-15)
-
-/* Errors from the server : */
-#define NATPMP_ERR_UNDEFINEDERROR (-49)
-#define NATPMP_ERR_NOTAUTHORIZED (-51)
-#define NATPMP_ERR_NETWORKFAILURE (-52)
-#define NATPMP_ERR_OUTOFRESOURCES (-53)
-
-/* NATPMP_TRYAGAIN : no data available for the moment. try again later */
-#define NATPMP_TRYAGAIN (-100)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* initnatpmp()
- * initialize a natpmp_t object
- * With forcegw=1 the gateway is not detected automaticaly.
- * Return values :
- * 0 = OK
- * NATPMP_ERR_INVALIDARGS
- * NATPMP_ERR_SOCKETERROR
- * NATPMP_ERR_FCNTLERROR
- * NATPMP_ERR_CANNOTGETGATEWAY
- * NATPMP_ERR_CONNECTERR */
-LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw);
-
-/* closenatpmp()
- * close resources associated with a natpmp_t object
- * Return values :
- * 0 = OK
- * NATPMP_ERR_INVALIDARGS
- * NATPMP_ERR_CLOSEERR */
-LIBSPEC int closenatpmp(natpmp_t * p);
-
-/* sendpublicaddressrequest()
- * send a public address NAT-PMP request to the network gateway
- * Return values :
- * 2 = OK (size of the request)
- * NATPMP_ERR_INVALIDARGS
- * NATPMP_ERR_SENDERR */
-LIBSPEC int sendpublicaddressrequest(natpmp_t * p);
-
-/* sendnewportmappingrequest()
- * send a new port mapping NAT-PMP request to the network gateway
- * Arguments :
- * protocol is either NATPMP_PROTOCOL_TCP or NATPMP_PROTOCOL_UDP,
- * lifetime is in seconds.
- * To remove a port mapping, set lifetime to zero.
- * To remove all port mappings to the host, set lifetime and both ports
- * to zero.
- * Return values :
- * 12 = OK (size of the request)
- * NATPMP_ERR_INVALIDARGS
- * NATPMP_ERR_SENDERR */
-LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol,
- uint16_t privateport, uint16_t publicport,
- uint32_t lifetime);
-
-/* getnatpmprequesttimeout()
- * fills the timeval structure with the timeout duration of the
- * currently pending NAT-PMP request.
- * Return values :
- * 0 = OK
- * NATPMP_ERR_INVALIDARGS
- * NATPMP_ERR_GETTIMEOFDAYERR
- * NATPMP_ERR_NOPENDINGREQ */
-LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout);
-
-/* readnatpmpresponseorretry()
- * fills the natpmpresp_t structure if possible
- * Return values :
- * 0 = OK
- * NATPMP_TRYAGAIN
- * NATPMP_ERR_INVALIDARGS
- * NATPMP_ERR_NOPENDINGREQ
- * NATPMP_ERR_NOGATEWAYSUPPORT
- * NATPMP_ERR_RECVFROM
- * NATPMP_ERR_WRONGPACKETSOURCE
- * NATPMP_ERR_UNSUPPORTEDVERSION
- * NATPMP_ERR_UNSUPPORTEDOPCODE
- * NATPMP_ERR_NOTAUTHORIZED
- * NATPMP_ERR_NETWORKFAILURE
- * NATPMP_ERR_OUTOFRESOURCES
- * NATPMP_ERR_UNSUPPORTEDOPCODE
- * NATPMP_ERR_UNDEFINEDERROR */
-LIBSPEC int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response);
-
-#ifdef ENABLE_STRNATPMPERR
-LIBSPEC const char * strnatpmperr(int t);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/* $Id: wingettimeofday.c,v 1.4 2011/07/15 08:30:11 nanard Exp $ */
-/* libnatpmp
-Copyright (c) 2007-2011, Thomas BERNARD
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-*/
-#ifdef WIN32
-#if defined(_MSC_VER)
-struct timeval {
- long tv_sec;
- long tv_usec;
-};
-#else
-#include <sys/time.h>
-#endif
-
-typedef struct _FILETIME {
- unsigned long dwLowDateTime;
- unsigned long dwHighDateTime;
-} FILETIME;
-
-void __stdcall GetSystemTimeAsFileTime(FILETIME*);
-
-int gettimeofday(struct timeval* p, void* tz /* IGNORED */) {
- union {
- long long ns100; /*time since 1 Jan 1601 in 100ns units */
- FILETIME ft;
- } _now;
-
- if(!p)
- return -1;
- GetSystemTimeAsFileTime( &(_now.ft) );
- p->tv_usec =(long)((_now.ns100 / 10LL) % 1000000LL );
- p->tv_sec = (long)((_now.ns100-(116444736000000000LL))/10000000LL);
- return 0;
-}
-#endif
-
+++ /dev/null
-/* $Id: wingettimeofday.h,v 1.2 2011/07/15 08:30:11 nanard Exp $ */
-/* libnatpmp
-Copyright (c) 2007-2011, Thomas BERNARD
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-*/
-#ifndef __WINGETTIMEOFDAY_H__
-#define __WINGETTIMEOFDAY_H__
-#ifdef WIN32
-#if defined(_MSC_VER)
-#include <time.h>
-#else
-#include <sys/time.h>
-#endif
-int gettimeofday(struct timeval* p, void* tz /* IGNORED */);
-#endif
-#endif
+++ /dev/null
-Copyright (c) 2010 BitTorrent, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+++ /dev/null
-AM_CPPFLAGS = -fno-exceptions -fno-rtti -ansi -DPOSIX
-
-noinst_LIBRARIES = libutp.a
-libutp_a_SOURCES = utp.cpp utp_utils.cpp
-noinst_HEADERS = StdAfx.h templates.h utp_config_example.h utp.h utp_config.h utp_utils.h utypes.h
-EXTRA_DIST = LICENSE README.md
+++ /dev/null
-# libutp - The uTorrent Transport Protocol library.
-Copyright (c) 2010 BitTorrent, Inc.
-
-uTP is a TCP-like implementation of [LEDBAT][ledbat] documented as a BitTorrent
-extension in [BEP-29][bep29]. uTP provides provides reliable, ordered delivery
-while maintaining minimum extra delay. It is implemented on top of UDP to be
-cross-platform and functional today. As a result, uTP is the primary transport
-for uTorrent peer-to-peer connections.
-
-uTP is written in C++, but the external interface is strictly C (ANSI C89).
-
-## The Interface
-
-The uTP socket interface is a bit different from the Berkeley socket API to
-avoid the need for our own select() implementation, and to make it easier to
-write event-based code with minimal buffering.
-
-When you create a uTP socket, you register a set of callbacks. Most notably, the
-on_read callback is a reactive callback which occurs when bytes arrive off the
-network. The write side of the socket is proactive, and you call UTP_Write to
-indicate the number of bytes you wish to write. As packets are created, the
-on_write callback is called for each packet, so you can fill the buffers with
-data.
-
-The libutp interface is not thread-safe. It was designed for use in a
-single-threaded asyncronous context, although with proper synchronization
-it may be used from a multi-threaded environment as well.
-
-See utp.h for more details and other API documentation.
-
-## Examples
-
-See the utp_test and utp_file directories for examples.
-
-## Building
-
-uTP has been known to build on Windows with MSVC and on linux and OS X with gcc.
-On Windows, use the MSVC project files (utp.sln, and friends). On other platforms,
-building the shared library is as simple as:
-
- make
-
-To build one of the examples, which will statically link in everything it needs
-from libutp:
-
- cd utp_test && make
-
-## Packaging and API
-
-The libutp API is considered unstable, and probably always will be. We encourage
-you to test with the version of libutp you have, and be mindful when upgrading.
-For this reason, it is probably also a good idea to bundle libutp with your
-application.
-
-## License
-
-libutp is released under the [MIT][lic] license.
-
-## Related Work
-
-Research and analysis of congestion control mechanisms can be found [here.][survey]
-
-[ledbat]: http://datatracker.ietf.org/wg/ledbat/charter/
-[bep29]: http://www.bittorrent.org/beps/bep_0029.html
-[lic]: http://www.opensource.org/licenses/mit-license.php
-[survey]: http://datatracker.ietf.org/doc/draft-ietf-ledbat-survey/
+++ /dev/null
-#if !defined(AFX_STDAFX_H__C1470942_E9DA_4913_BEF1_9BA7584E595B__INCLUDED_)\r
-#define AFX_STDAFX_H__C1470942_E9DA_4913_BEF1_9BA7584E595B__INCLUDED_\r
-\r
-#if _MSC_VER > 1000\r
-#pragma once\r
-#endif // _MSC_VER > 1000\r
-\r
-// I don't have anything to put here, but some projects use precompiled headers,\r
-// so I include StdAfx.h anyway, so they don't have to edit the files to compile normally.\r
-\r
-#endif // !defined(AFX_STDAFX_H__C1470942_E9DA_4913_BEF1_9BA7584E595B__INCLUDED_)\r
+++ /dev/null
-#ifndef __TEMPLATES_H__\r
-#define __TEMPLATES_H__\r
-\r
-#include "utypes.h"\r
-#include <assert.h>\r
-\r
-#if defined(POSIX)\r
-/* Allow over-writing FORCEINLINE from makefile because gcc 3.4.4 for buffalo\r
- doesn't seem to support __attribute__((always_inline)) in -O0 build\r
- (strangely, it works in -Os build) */\r
-#ifndef FORCEINLINE\r
-// The always_inline attribute asks gcc to inline the function even if no optimization is being requested.\r
-// This macro should be used exclusive-or with the inline directive (use one or the other but not both)\r
-// since Microsoft uses __forceinline to also mean inline,\r
-// and this code is following a Microsoft compatibility model.\r
-// Just setting the attribute without also specifying the inline directive apparently won't inline the function,\r
-// as evidenced by multiply-defined symbols found at link time.\r
-#define FORCEINLINE inline __attribute__((always_inline))\r
-#endif\r
-#endif\r
-\r
-#ifdef __GNUC__\r
-// Used for gcc tool chains accepting but not supporting pragma pack\r
-// See http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html\r
-#define PACKED_ATTRIBUTE __attribute__((__packed__))\r
-#else\r
-#define PACKED_ATTRIBUTE\r
-#endif\r
-\r
-#ifdef __GNUC__\r
-#define ALIGNED_ATTRIBUTE(x) __attribute__((aligned (x)))\r
-#else\r
-#define ALIGNED_ATTRIBUTE(x)\r
-#endif\r
-\r
-// Utility templates\r
-#undef min\r
-#undef max\r
-\r
-template <typename T> static inline T min(T a, T b) { if (a < b) return a; return b; }\r
-template <typename T> static inline T max(T a, T b) { if (a > b) return a; return b; }\r
-\r
-template <typename T> static inline T min(T a, T b, T c) { return min(min(a,b),c); }\r
-template <typename T> static inline T max(T a, T b, T c) { return max(max(a,b),c); }\r
-template <typename T> static inline T clamp(T v, T mi, T ma)\r
-{\r
- if (v > ma) v = ma;\r
- if (v < mi) v = mi;\r
- return v;\r
-}\r
-\r
-#if (defined(__SVR4) && defined(__sun))\r
-#pragma pack(1)\r
-#else\r
-#pragma pack(push,1)\r
-#endif\r
-\r
-namespace aux\r
-{\r
- FORCEINLINE uint16 host_to_network(uint16 i) { return htons(i); }\r
- FORCEINLINE uint32 host_to_network(uint32 i) { return htonl(i); }\r
- FORCEINLINE int32 host_to_network(int32 i) { return htonl(i); }\r
- FORCEINLINE uint16 network_to_host(uint16 i) { return ntohs(i); }\r
- FORCEINLINE uint32 network_to_host(uint32 i) { return ntohl(i); }\r
- FORCEINLINE int32 network_to_host(int32 i) { return ntohl(i); }\r
-}\r
-\r
-template <class T>\r
-struct PACKED_ATTRIBUTE big_endian\r
-{\r
- T operator=(T i) { m_integer = aux::host_to_network(i); return i; }\r
- operator T() const { return aux::network_to_host(m_integer); }\r
-private:\r
- T m_integer;\r
-};\r
-\r
-typedef big_endian<int32> int32_big;\r
-typedef big_endian<uint32> uint32_big;\r
-typedef big_endian<uint16> uint16_big;\r
-\r
-#if (defined(__SVR4) && defined(__sun))\r
-#pragma pack(0)\r
-#else\r
-#pragma pack(pop)\r
-#endif\r
-\r
-template<typename T> static inline void zeromem(T *a, size_t count = 1) { memset(a, 0, count * sizeof(T)); }\r
-\r
-typedef int SortCompareProc(const void *, const void *);\r
-\r
-template<typename T> static FORCEINLINE void QuickSortT(T *base, size_t num, int (*comp)(const T *, const T *)) { qsort(base, num, sizeof(T), (SortCompareProc*)comp); }\r
-\r
-\r
-// WARNING: The template parameter MUST be a POD type!\r
-template <typename T, size_t minsize = 16> class Array {\r
-protected:\r
- T *mem;\r
- size_t alloc,count;\r
-\r
-public:\r
- Array(size_t init) { Init(init); }\r
- Array() { Init(); }\r
- ~Array() { Free(); }\r
-\r
- void inline Init() { mem = NULL; alloc = count = 0; }\r
- void inline Init(size_t init) { Init(); if (init) Resize(init); }\r
- size_t inline GetCount() const { return count; }\r
- size_t inline GetAlloc() const { return alloc; }\r
- void inline SetCount(size_t c) { count = c; }\r
-\r
- inline T& operator[](size_t offset) { assert(offset ==0 || offset<alloc); return mem[offset]; }\r
- inline const T& operator[](size_t offset) const { assert(offset ==0 || offset<alloc); return mem[offset]; }\r
-\r
- void inline Resize(size_t a) {\r
- if (a == 0) { free(mem); Init(); }\r
- else { mem = (T*)realloc(mem, (alloc=a) * sizeof(T)); }\r
- }\r
-\r
- void Grow() { Resize(::max<size_t>(minsize, alloc * 2)); }\r
-\r
- inline size_t Append(const T &t) {\r
- if (count >= alloc) Grow();\r
- size_t r=count++;\r
- mem[r] = t;\r
- return r;\r
- }\r
-\r
- T inline &Append() {\r
- if (count >= alloc) Grow();\r
- return mem[count++];\r
- }\r
-\r
- void inline Compact() {\r
- Resize(count);\r
- }\r
-\r
- void inline Free() {\r
- free(mem);\r
- Init();\r
- }\r
-\r
- void inline Clear() {\r
- count = 0;\r
- }\r
-\r
- bool inline MoveUpLast(size_t index) {\r
- assert(index < count);\r
- size_t c = --count;\r
- if (index != c) {\r
- mem[index] = mem[c];\r
- return true;\r
- }\r
- return false;\r
- }\r
-\r
- bool inline MoveUpLastExist(const T &v) {\r
- return MoveUpLast(LookupElementExist(v));\r
- }\r
-\r
- size_t inline LookupElement(const T &v) const {\r
- for(size_t i = 0; i != count; i++)\r
- if (mem[i] == v)\r
- return i;\r
- return (size_t) -1;\r
- }\r
-\r
- bool inline HasElement(const T &v) const {\r
- return LookupElement(v) != -1;\r
- }\r
-\r
- typedef int SortCompareProc(const T *a, const T *b);\r
-\r
- void Sort(SortCompareProc* proc, size_t start, size_t end) {\r
- QuickSortT(&mem[start], end - start, proc);\r
- }\r
-\r
- void Sort(SortCompareProc* proc, size_t start) {\r
- Sort(proc, start, count);\r
- }\r
-\r
- void Sort(SortCompareProc* proc) {\r
- Sort(proc, 0, count);\r
- }\r
-};\r
-\r
-#endif //__TEMPLATES_H__\r
+++ /dev/null
-#include <StdAfx.h>\r
-\r
-#include "utp.h"\r
-#include "templates.h"\r
-\r
-#include <stdio.h>\r
-#include <assert.h>\r
-#include <string.h>\r
-#include <string.h>\r
-#include <stdlib.h>\r
-#include <errno.h>\r
-#include <limits.h> // for UINT_MAX\r
-\r
-#ifdef WIN32\r
-#include "win32_inet_ntop.h"\r
-\r
-// newer versions of MSVC define these in errno.h\r
-#ifndef ECONNRESET\r
-#define ECONNRESET WSAECONNRESET\r
-#define EMSGSIZE WSAEMSGSIZE\r
-#define ECONNREFUSED WSAECONNREFUSED\r
-#define ETIMEDOUT WSAETIMEDOUT\r
-#endif\r
-#endif\r
-\r
-#ifdef POSIX\r
-typedef sockaddr_storage SOCKADDR_STORAGE;\r
-#endif // POSIX\r
-\r
-// number of bytes to increase max window size by, per RTT. This is\r
-// scaled down linearly proportional to off_target. i.e. if all packets\r
-// in one window have 0 delay, window size will increase by this number.\r
-// Typically it's less. TCP increases one MSS per RTT, which is 1500\r
-#define MAX_CWND_INCREASE_BYTES_PER_RTT 3000\r
-#define CUR_DELAY_SIZE 3\r
-// experiments suggest that a clock skew of 10 ms per 325 seconds\r
-// is not impossible. Reset delay_base every 13 minutes. The clock\r
-// skew is dealt with by observing the delay base in the other\r
-// direction, and adjusting our own upwards if the opposite direction\r
-// delay base keeps going down\r
-#define DELAY_BASE_HISTORY 13\r
-#define MAX_WINDOW_DECAY 100 // ms\r
-\r
-#define REORDER_BUFFER_SIZE 32\r
-#define REORDER_BUFFER_MAX_SIZE 511\r
-#define OUTGOING_BUFFER_MAX_SIZE 511\r
-\r
-#define PACKET_SIZE 350\r
-\r
-// this is the minimum max_window value. It can never drop below this\r
-#define MIN_WINDOW_SIZE 10\r
-\r
-// when window sizes are smaller than one packet_size, this\r
-// will pace the packets to average at the given window size\r
-// if it's not set, it will simply not send anything until\r
-// there's a timeout\r
-#define USE_PACKET_PACING 1\r
-\r
-// if we receive 4 or more duplicate acks, we resend the packet\r
-// that hasn't been acked yet\r
-#define DUPLICATE_ACKS_BEFORE_RESEND 3\r
-\r
-#define DELAYED_ACK_BYTE_THRESHOLD 2400 // bytes\r
-#define DELAYED_ACK_TIME_THRESHOLD 100 // milliseconds\r
-\r
-#define RST_INFO_TIMEOUT 10000\r
-#define RST_INFO_LIMIT 1000\r
-// 29 seconds determined from measuring many home NAT devices\r
-#define KEEPALIVE_INTERVAL 29000\r
-\r
-\r
-#define SEQ_NR_MASK 0xFFFF\r
-#define ACK_NR_MASK 0xFFFF\r
-\r
-#define DIV_ROUND_UP(num, denom) ((num + denom - 1) / denom)\r
-\r
-#include "utp_utils.h"\r
-#include "utp_config.h"\r
-\r
-#define LOG_UTP if (g_log_utp) utp_log\r
-#define LOG_UTPV if (g_log_utp_verbose) utp_log\r
-\r
-uint32 g_current_ms;\r
-\r
-// The totals are derived from the following data:\r
-// 45: IPv6 address including embedded IPv4 address\r
-// 11: Scope Id\r
-// 2: Brackets around IPv6 address when port is present\r
-// 6: Port (including colon)\r
-// 1: Terminating null byte\r
-char addrbuf[65];\r
-char addrbuf2[65];\r
-#define addrfmt(x, s) x.fmt(s, sizeof(s))\r
-\r
-#if (defined(__SVR4) && defined(__sun))\r
-#pragma pack(1)\r
-#else\r
-#pragma pack(push,1)\r
-#endif\r
-\r
-struct PACKED_ATTRIBUTE PackedSockAddr {\r
-\r
- // The values are always stored here in network byte order\r
- union {\r
- byte _in6[16]; // IPv6\r
- uint16 _in6w[8]; // IPv6, word based (for convenience)\r
- uint32 _in6d[4]; // Dword access\r
- in6_addr _in6addr; // For convenience\r
- } _in;\r
-\r
- // Host byte order\r
- uint16 _port;\r
-\r
-#define _sin4 _in._in6d[3] // IPv4 is stored where it goes if mapped\r
-\r
-#define _sin6 _in._in6\r
-#define _sin6w _in._in6w\r
-#define _sin6d _in._in6d\r
-\r
- byte get_family() const\r
- {\r
- return (IN6_IS_ADDR_V4MAPPED(&_in._in6addr) != 0) ? AF_INET : AF_INET6;\r
- }\r
-\r
- bool operator==(const PackedSockAddr& rhs) const\r
- {\r
- if (&rhs == this)\r
- return true;\r
- if (_port != rhs._port)\r
- return false;\r
- return memcmp(_sin6, rhs._sin6, sizeof(_sin6)) == 0;\r
- }\r
- bool operator!=(const PackedSockAddr& rhs) const { return !(*this == rhs); }\r
-\r
- PackedSockAddr(const SOCKADDR_STORAGE* sa, socklen_t len)\r
- {\r
- if (sa->ss_family == AF_INET) {\r
- assert(len >= sizeof(sockaddr_in));\r
- const sockaddr_in *sin = (sockaddr_in*)sa;\r
- _sin6w[0] = 0;\r
- _sin6w[1] = 0;\r
- _sin6w[2] = 0;\r
- _sin6w[3] = 0;\r
- _sin6w[4] = 0;\r
- _sin6w[5] = 0xffff;\r
- _sin4 = sin->sin_addr.s_addr;\r
- _port = ntohs(sin->sin_port);\r
- } else {\r
- assert(len >= sizeof(sockaddr_in6));\r
- const sockaddr_in6 *sin6 = (sockaddr_in6*)sa;\r
- _in._in6addr = sin6->sin6_addr;\r
- _port = ntohs(sin6->sin6_port);\r
- }\r
- }\r
-\r
- SOCKADDR_STORAGE get_sockaddr_storage(socklen_t *len = NULL) const\r
- {\r
- SOCKADDR_STORAGE sa;\r
- const byte family = get_family();\r
- if (family == AF_INET) {\r
- sockaddr_in *sin = (sockaddr_in*)&sa;\r
- if (len) *len = sizeof(sockaddr_in);\r
- memset(sin, 0, sizeof(sockaddr_in));\r
- sin->sin_family = family;\r
- sin->sin_port = htons(_port);\r
- sin->sin_addr.s_addr = _sin4;\r
- } else {\r
- sockaddr_in6 *sin6 = (sockaddr_in6*)&sa;\r
- memset(sin6, 0, sizeof(sockaddr_in6));\r
- if (len) *len = sizeof(sockaddr_in6);\r
- sin6->sin6_family = family;\r
- sin6->sin6_addr = _in._in6addr;\r
- sin6->sin6_port = htons(_port);\r
- }\r
- return sa;\r
- }\r
-\r
- cstr fmt(str s, size_t len) const\r
- {\r
- memset(s, 0, len);\r
- const byte family = get_family();\r
- str i;\r
- if (family == AF_INET) {\r
- inet_ntop(family, (uint32*)&_sin4, s, len);\r
- i = s;\r
- while (*++i) {}\r
- } else {\r
- i = s;\r
- *i++ = '[';\r
- inet_ntop(family, (in6_addr*)&_in._in6addr, i, len-1);\r
- while (*++i) {}\r
- *i++ = ']';\r
- }\r
- snprintf(i, len - (i-s), ":%u", _port);\r
- return s;\r
- }\r
-} ALIGNED_ATTRIBUTE(4);\r
-\r
-struct PACKED_ATTRIBUTE RST_Info {\r
- PackedSockAddr addr;\r
- uint32 connid;\r
- uint32 timestamp;\r
- uint16 ack_nr;\r
-};\r
-\r
-// these packet sizes are including the uTP header wich\r
-// is either 20 or 23 bytes depending on version\r
-#define PACKET_SIZE_EMPTY_BUCKET 0\r
-#define PACKET_SIZE_EMPTY 23\r
-#define PACKET_SIZE_SMALL_BUCKET 1\r
-#define PACKET_SIZE_SMALL 373\r
-#define PACKET_SIZE_MID_BUCKET 2\r
-#define PACKET_SIZE_MID 723\r
-#define PACKET_SIZE_BIG_BUCKET 3\r
-#define PACKET_SIZE_BIG 1400\r
-#define PACKET_SIZE_HUGE_BUCKET 4\r
-\r
-struct PACKED_ATTRIBUTE PacketFormat {\r
- // connection ID\r
- uint32_big connid;\r
- uint32_big tv_sec;\r
- uint32_big tv_usec;\r
- uint32_big reply_micro;\r
- // receive window size in PACKET_SIZE chunks\r
- byte windowsize;\r
- // Type of the first extension header\r
- byte ext;\r
- // Flags\r
- byte flags;\r
- // Sequence number\r
- uint16_big seq_nr;\r
- // Acknowledgment number\r
- uint16_big ack_nr;\r
-};\r
-\r
-struct PACKED_ATTRIBUTE PacketFormatAck {\r
- PacketFormat pf;\r
- byte ext_next;\r
- byte ext_len;\r
- byte acks[4];\r
-};\r
-\r
-struct PACKED_ATTRIBUTE PacketFormatExtensions {\r
- PacketFormat pf;\r
- byte ext_next;\r
- byte ext_len;\r
- byte extensions[8];\r
-};\r
-\r
-struct PACKED_ATTRIBUTE PacketFormatV1 {\r
- // packet_type (4 high bits)\r
- // protocol version (4 low bits)\r
- byte ver_type;\r
- byte version() const { return ver_type & 0xf; }\r
- byte type() const { return ver_type >> 4; }\r
- void set_version(byte v) { ver_type = (ver_type & 0xf0) | (v & 0xf); }\r
- void set_type(byte t) { ver_type = (ver_type & 0xf) | (t << 4); }\r
-\r
- // Type of the first extension header\r
- byte ext;\r
- // connection ID\r
- uint16_big connid;\r
- uint32_big tv_usec;\r
- uint32_big reply_micro;\r
- // receive window size in bytes\r
- uint32_big windowsize;\r
- // Sequence number\r
- uint16_big seq_nr;\r
- // Acknowledgment number\r
- uint16_big ack_nr;\r
-};\r
-\r
-struct PACKED_ATTRIBUTE PacketFormatAckV1 {\r
- PacketFormatV1 pf;\r
- byte ext_next;\r
- byte ext_len;\r
- byte acks[4];\r
-};\r
-\r
-struct PACKED_ATTRIBUTE PacketFormatExtensionsV1 {\r
- PacketFormatV1 pf;\r
- byte ext_next;\r
- byte ext_len;\r
- byte extensions[8];\r
-};\r
-\r
-#if (defined(__SVR4) && defined(__sun))\r
-#pragma pack(0)\r
-#else\r
-#pragma pack(pop)\r
-#endif\r
-\r
-enum {\r
- ST_DATA = 0, // Data packet.\r
- ST_FIN = 1, // Finalize the connection. This is the last packet.\r
- ST_STATE = 2, // State packet. Used to transmit an ACK with no data.\r
- ST_RESET = 3, // Terminate connection forcefully.\r
- ST_SYN = 4, // Connect SYN\r
- ST_NUM_STATES, // used for bounds checking\r
-};\r
-\r
-static const cstr flagnames[] = {\r
- "ST_DATA","ST_FIN","ST_STATE","ST_RESET","ST_SYN"\r
-};\r
-\r
-enum CONN_STATE {\r
- CS_IDLE = 0,\r
- CS_SYN_SENT = 1,\r
- CS_CONNECTED = 2,\r
- CS_CONNECTED_FULL = 3,\r
- CS_GOT_FIN = 4,\r
- CS_DESTROY_DELAY = 5,\r
- CS_FIN_SENT = 6,\r
- CS_RESET = 7,\r
- CS_DESTROY = 8,\r
-};\r
-\r
-static const cstr statenames[] = {\r
- "IDLE","SYN_SENT","CONNECTED","CONNECTED_FULL","GOT_FIN","DESTROY_DELAY","FIN_SENT","RESET","DESTROY"\r
-};\r
-\r
-struct OutgoingPacket {\r
- size_t length;\r
- size_t payload;\r
- uint64 time_sent; // microseconds\r
- uint transmissions:31;\r
- bool need_resend:1;\r
- byte data[1];\r
-};\r
-\r
-void no_read(void *socket, const byte *bytes, size_t count) {}\r
-void no_write(void *socket, byte *bytes, size_t count) {}\r
-size_t no_rb_size(void *socket) { return 0; }\r
-void no_state(void *socket, int state) {}\r
-void no_error(void *socket, int errcode) {}\r
-void no_overhead(void *socket, bool send, size_t count, int type) {}\r
-\r
-UTPFunctionTable zero_funcs = {\r
- &no_read,\r
- &no_write,\r
- &no_rb_size,\r
- &no_state,\r
- &no_error,\r
- &no_overhead,\r
-};\r
-\r
-struct SizableCircularBuffer {\r
- // This is the mask. Since it's always a power of 2, adding 1 to this value will return the size.\r
- size_t mask;\r
- // This is the elements that the circular buffer points to\r
- void **elements;\r
-\r
- void *get(size_t i) { assert(elements); return elements ? elements[i & mask] : NULL; }\r
- void put(size_t i, void *data) { assert(elements); elements[i&mask] = data; }\r
-\r
- void grow(size_t item, size_t index);\r
- void ensure_size(size_t item, size_t index) { if (index > mask) grow(item, index); }\r
- size_t size() { return mask + 1; }\r
-};\r
-\r
-static struct UTPGlobalStats _global_stats;\r
-\r
-// Item contains the element we want to make space for\r
-// index is the index in the list.\r
-void SizableCircularBuffer::grow(size_t item, size_t index)\r
-{\r
- // Figure out the new size.\r
- size_t size = mask + 1;\r
- do size *= 2; while (index >= size);\r
-\r
- // Allocate the new buffer\r
- void **buf = (void**)calloc(size, sizeof(void*));\r
-\r
- size--;\r
-\r
- // Copy elements from the old buffer to the new buffer\r
- for (size_t i = 0; i <= mask; i++) {\r
- buf[(item - index + i) & size] = get(item - index + i);\r
- }\r
-\r
- // Swap to the newly allocated buffer\r
- mask = size;\r
- free(elements);\r
- elements = buf;\r
-}\r
-\r
-// compare if lhs is less than rhs, taking wrapping\r
-// into account. if lhs is close to UINT_MAX and rhs\r
-// is close to 0, lhs is assumed to have wrapped and\r
-// considered smaller\r
-bool wrapping_compare_less(uint32 lhs, uint32 rhs)\r
-{\r
- // distance walking from lhs to rhs, downwards\r
- const uint32 dist_down = lhs - rhs;\r
- // distance walking from lhs to rhs, upwards\r
- const uint32 dist_up = rhs - lhs;\r
-\r
- // if the distance walking up is shorter, lhs\r
- // is less than rhs. If the distance walking down\r
- // is shorter, then rhs is less than lhs\r
- return dist_up < dist_down;\r
-}\r
-\r
-struct DelayHist {\r
- uint32 delay_base;\r
-\r
- // this is the history of delay samples,\r
- // normalized by using the delay_base. These\r
- // values are always greater than 0 and measures\r
- // the queuing delay in microseconds\r
- uint32 cur_delay_hist[CUR_DELAY_SIZE];\r
- size_t cur_delay_idx;\r
-\r
- // this is the history of delay_base. It's\r
- // a number that doesn't have an absolute meaning\r
- // only relative. It doesn't make sense to initialize\r
- // it to anything other than values relative to\r
- // what's been seen in the real world.\r
- uint32 delay_base_hist[DELAY_BASE_HISTORY];\r
- size_t delay_base_idx;\r
- // the time when we last stepped the delay_base_idx\r
- uint32 delay_base_time;\r
-\r
- bool delay_base_initialized;\r
-\r
- void clear()\r
- {\r
- delay_base_initialized = false;\r
- delay_base = 0;\r
- cur_delay_idx = 0;\r
- delay_base_idx = 0;\r
- delay_base_time = g_current_ms;\r
- for (size_t i = 0; i < CUR_DELAY_SIZE; i++) {\r
- cur_delay_hist[i] = 0;\r
- }\r
- for (size_t i = 0; i < DELAY_BASE_HISTORY; i++) {\r
- delay_base_hist[i] = 0;\r
- }\r
- }\r
-\r
- void shift(const uint32 offset)\r
- {\r
- // the offset should never be "negative"\r
- // assert(offset < 0x10000000);\r
-\r
- // increase all of our base delays by this amount\r
- // this is used to take clock skew into account\r
- // by observing the other side's changes in its base_delay\r
- for (size_t i = 0; i < DELAY_BASE_HISTORY; i++) {\r
- delay_base_hist[i] += offset;\r
- }\r
- delay_base += offset;\r
- }\r
-\r
- void add_sample(const uint32 sample)\r
- {\r
- // The two clocks (in the two peers) are assumed not to\r
- // progress at the exact same rate. They are assumed to be\r
- // drifting, which causes the delay samples to contain\r
- // a systematic error, either they are under-\r
- // estimated or over-estimated. This is why we update the\r
- // delay_base every two minutes, to adjust for this.\r
-\r
- // This means the values will keep drifting and eventually wrap.\r
- // We can cross the wrapping boundry in two directions, either\r
- // going up, crossing the highest value, or going down, crossing 0.\r
-\r
- // if the delay_base is close to the max value and sample actually\r
- // wrapped on the other end we would see something like this:\r
- // delay_base = 0xffffff00, sample = 0x00000400\r
- // sample - delay_base = 0x500 which is the correct difference\r
-\r
- // if the delay_base is instead close to 0, and we got an even lower\r
- // sample (that will eventually update the delay_base), we may see\r
- // something like this:\r
- // delay_base = 0x00000400, sample = 0xffffff00\r
- // sample - delay_base = 0xfffffb00\r
- // this needs to be interpreted as a negative number and the actual\r
- // recorded delay should be 0.\r
-\r
- // It is important that all arithmetic that assume wrapping\r
- // is done with unsigned intergers. Signed integers are not guaranteed\r
- // to wrap the way unsigned integers do. At least GCC takes advantage\r
- // of this relaxed rule and won't necessarily wrap signed ints.\r
-\r
- // remove the clock offset and propagation delay.\r
- // delay base is min of the sample and the current\r
- // delay base. This min-operation is subject to wrapping\r
- // and care needs to be taken to correctly choose the\r
- // true minimum.\r
-\r
- // specifically the problem case is when delay_base is very small\r
- // and sample is very large (because it wrapped past zero), sample\r
- // needs to be considered the smaller\r
-\r
- if (!delay_base_initialized) {\r
- // delay_base being 0 suggests that we haven't initialized\r
- // it or its history with any real measurements yet. Initialize\r
- // everything with this sample.\r
- for (size_t i = 0; i < DELAY_BASE_HISTORY; i++) {\r
- // if we don't have a value, set it to the current sample\r
- delay_base_hist[i] = sample;\r
- continue;\r
- }\r
- delay_base = sample;\r
- delay_base_initialized = true;\r
- }\r
-\r
- if (wrapping_compare_less(sample, delay_base_hist[delay_base_idx])) {\r
- // sample is smaller than the current delay_base_hist entry\r
- // update it\r
- delay_base_hist[delay_base_idx] = sample;\r
- }\r
-\r
- // is sample lower than delay_base? If so, update delay_base\r
- if (wrapping_compare_less(sample, delay_base)) {\r
- // sample is smaller than the current delay_base\r
- // update it\r
- delay_base = sample;\r
- }\r
- \r
- // this operation may wrap, and is supposed to\r
- const uint32 delay = sample - delay_base;\r
- // sanity check. If this is triggered, something fishy is going on\r
- // it means the measured sample was greater than 32 seconds!\r
-// assert(delay < 0x2000000);\r
-\r
- cur_delay_hist[cur_delay_idx] = delay;\r
- cur_delay_idx = (cur_delay_idx + 1) % CUR_DELAY_SIZE;\r
-\r
- // once every minute\r
- if (g_current_ms - delay_base_time > 60 * 1000) {\r
- delay_base_time = g_current_ms;\r
- delay_base_idx = (delay_base_idx + 1) % DELAY_BASE_HISTORY;\r
- // clear up the new delay base history spot by initializing\r
- // it to the current sample, then update it \r
- delay_base_hist[delay_base_idx] = sample;\r
- delay_base = delay_base_hist[0];\r
- // Assign the lowest delay in the last 2 minutes to delay_base\r
- for (size_t i = 0; i < DELAY_BASE_HISTORY; i++) {\r
- if (wrapping_compare_less(delay_base_hist[i], delay_base))\r
- delay_base = delay_base_hist[i];\r
- }\r
- }\r
- }\r
-\r
- uint32 get_value()\r
- {\r
- uint32 value = UINT_MAX;\r
- for (size_t i = 0; i < CUR_DELAY_SIZE; i++) {\r
- value = min<uint32>(cur_delay_hist[i], value);\r
- }\r
- // value could be UINT_MAX if we have no samples yet...\r
- return value;\r
- }\r
-};\r
-\r
-struct UTPSocket {\r
- PackedSockAddr addr;\r
-\r
- size_t idx;\r
-\r
- uint16 reorder_count;\r
- byte duplicate_ack;\r
-\r
- // the number of bytes we've received but not acked yet\r
- size_t bytes_since_ack;\r
-\r
- // the number of packets in the send queue. Packets that haven't\r
- // yet been sent count as well as packets marked as needing resend\r
- // the oldest un-acked packet in the send queue is seq_nr - cur_window_packets\r
- uint16 cur_window_packets;\r
-\r
- // how much of the window is used, number of bytes in-flight\r
- // packets that have not yet been sent do not count, packets\r
- // that are marked as needing to be re-sent (due to a timeout)\r
- // don't count either\r
- size_t cur_window;\r
- // maximum window size, in bytes\r
- size_t max_window;\r
- // SO_SNDBUF setting, in bytes\r
- size_t opt_sndbuf;\r
- // SO_RCVBUF setting, in bytes\r
- size_t opt_rcvbuf;\r
-\r
- // Is a FIN packet in the reassembly buffer?\r
- bool got_fin:1;\r
- // Timeout procedure\r
- bool fast_timeout:1;\r
-\r
- // max receive window for other end, in bytes\r
- size_t max_window_user;\r
- // 0 = original uTP header, 1 = second revision\r
- byte version;\r
- CONN_STATE state;\r
- // TickCount when we last decayed window (wraps)\r
- int32 last_rwin_decay;\r
-\r
- // the sequence number of the FIN packet. This field is only set\r
- // when we have received a FIN, and the flag field has the FIN flag set.\r
- // it is used to know when it is safe to destroy the socket, we must have\r
- // received all packets up to this sequence number first.\r
- uint16 eof_pkt;\r
-\r
- // All sequence numbers up to including this have been properly received\r
- // by us\r
- uint16 ack_nr;\r
- // This is the sequence number for the next packet to be sent.\r
- uint16 seq_nr;\r
-\r
- uint16 timeout_seq_nr;\r
-\r
- // This is the sequence number of the next packet we're allowed to\r
- // do a fast resend with. This makes sure we only do a fast-resend\r
- // once per packet. We can resend the packet with this sequence number\r
- // or any later packet (with a higher sequence number).\r
- uint16 fast_resend_seq_nr;\r
-\r
- uint32 reply_micro;\r
-\r
- // the time when we need to send another ack. If there's\r
- // nothing to ack, this is a very large number\r
- uint32 ack_time;\r
-\r
- uint32 last_got_packet;\r
- uint32 last_sent_packet;\r
- uint32 last_measured_delay;\r
- uint32 last_maxed_out_window;\r
-\r
- // the last time we added send quota to the connection\r
- // when adding send quota, this is subtracted from the\r
- // current time multiplied by max_window / rtt\r
- // which is the current allowed send rate.\r
- int32 last_send_quota;\r
-\r
- // the number of bytes we are allowed to send on\r
- // this connection. If this is more than one packet\r
- // size when we run out of data to send, it is clamped\r
- // to the packet size\r
- // this value is multiplied by 100 in order to get\r
- // higher accuracy when dealing with low rates\r
- int32 send_quota;\r
-\r
- SendToProc *send_to_proc;\r
- void *send_to_userdata;\r
- UTPFunctionTable func;\r
- void *userdata;\r
-\r
- // Round trip time\r
- uint rtt;\r
- // Round trip time variance\r
- uint rtt_var;\r
- // Round trip timeout\r
- uint rto;\r
- DelayHist rtt_hist;\r
- uint retransmit_timeout;\r
- // The RTO timer will timeout here.\r
- uint rto_timeout;\r
- // When the window size is set to zero, start this timer. It will send a new packet every 30secs.\r
- uint32 zerowindow_time;\r
-\r
- uint32 conn_seed;\r
- // Connection ID for packets I receive\r
- uint32 conn_id_recv;\r
- // Connection ID for packets I send\r
- uint32 conn_id_send;\r
- // Last rcv window we advertised, in bytes\r
- size_t last_rcv_win;\r
-\r
- DelayHist our_hist;\r
- DelayHist their_hist;\r
-\r
- // extension bytes from SYN packet\r
- byte extensions[8];\r
-\r
- SizableCircularBuffer inbuf, outbuf;\r
-\r
-#ifdef _DEBUG\r
- // Public stats, returned by UTP_GetStats(). See utp.h\r
- UTPStats _stats;\r
-#endif // _DEBUG\r
-\r
- // Calculates the current receive window\r
- size_t get_rcv_window() const\r
- {\r
- // If we don't have a connection (such as during connection\r
- // establishment, always act as if we have an empty buffer).\r
- if (!userdata) return opt_rcvbuf;\r
-\r
- // Trim window down according to what's already in buffer.\r
- const size_t numbuf = func.get_rb_size(userdata);\r
- assert((int)numbuf >= 0);\r
- return opt_rcvbuf > numbuf ? opt_rcvbuf - numbuf : 0;\r
- }\r
-\r
- // Test if we're ready to decay max_window\r
- // XXX this breaks when spaced by > INT_MAX/2, which is 49\r
- // days; the failure mode in that case is we do an extra decay\r
- // or fail to do one when we really shouldn't.\r
- bool can_decay_win(int32 msec) const\r
- {\r
- return msec - last_rwin_decay >= MAX_WINDOW_DECAY;\r
- }\r
-\r
- // If we can, decay max window, returns true if we actually did so\r
- void maybe_decay_win()\r
- {\r
- if (can_decay_win(g_current_ms)) {\r
- // TCP uses 0.5\r
- max_window = (size_t)(max_window * .5);\r
- last_rwin_decay = g_current_ms;\r
- if (max_window < MIN_WINDOW_SIZE)\r
- max_window = MIN_WINDOW_SIZE;\r
- }\r
- }\r
-\r
- size_t get_header_size() const\r
- {\r
- return (version ? sizeof(PacketFormatV1) : sizeof(PacketFormat));\r
- }\r
-\r
- size_t get_header_extensions_size() const\r
- {\r
- return (version ? sizeof(PacketFormatExtensionsV1) : sizeof(PacketFormatExtensions));\r
- }\r
-\r
- void sent_ack()\r
- {\r
- ack_time = g_current_ms + 0x70000000;\r
- bytes_since_ack = 0;\r
- }\r
-\r
- size_t get_udp_mtu() const\r
- {\r
- socklen_t len;\r
- SOCKADDR_STORAGE sa = addr.get_sockaddr_storage(&len);\r
- return UTP_GetUDPMTU((const struct sockaddr *)&sa, len);\r
- }\r
-\r
- size_t get_udp_overhead() const\r
- {\r
- socklen_t len;\r
- SOCKADDR_STORAGE sa = addr.get_sockaddr_storage(&len);\r
- return UTP_GetUDPOverhead((const struct sockaddr *)&sa, len);\r
- }\r
-\r
- uint64 get_global_utp_bytes_sent() const\r
- {\r
- socklen_t len;\r
- SOCKADDR_STORAGE sa = addr.get_sockaddr_storage(&len);\r
- return UTP_GetGlobalUTPBytesSent((const struct sockaddr *)&sa, len);\r
- }\r
-\r
- size_t get_overhead() const\r
- {\r
- return get_udp_overhead() + get_header_size();\r
- }\r
-\r
- void send_data(PacketFormat* b, size_t length, bandwidth_type_t type);\r
-\r
- void send_ack(bool synack = false);\r
-\r
- void send_keep_alive();\r
-\r
- static void send_rst(SendToProc *send_to_proc, void *send_to_userdata,\r
- const PackedSockAddr &addr, uint32 conn_id_send,\r
- uint16 ack_nr, uint16 seq_nr, byte version);\r
-\r
- void send_packet(OutgoingPacket *pkt);\r
-\r
- bool is_writable(size_t to_write);\r
-\r
- bool flush_packets();\r
-\r
- void write_outgoing_packet(size_t payload, uint flags);\r
-\r
- void update_send_quota();\r
-\r
-#ifdef _DEBUG\r
- void check_invariant();\r
-#endif\r
-\r
- void check_timeouts();\r
-\r
- int ack_packet(uint16 seq);\r
-\r
- size_t selective_ack_bytes(uint base, const byte* mask, byte len, int64& min_rtt);\r
-\r
- void selective_ack(uint base, const byte *mask, byte len);\r
-\r
- void apply_ledbat_ccontrol(size_t bytes_acked, uint32 actual_delay, int64 min_rtt);\r
-\r
- size_t get_packet_size();\r
-};\r
-\r
-Array<RST_Info> g_rst_info;\r
-Array<UTPSocket*> g_utp_sockets;\r
-\r
-static void UTP_RegisterSentPacket(size_t length) {\r
- if (length <= PACKET_SIZE_MID) {\r
- if (length <= PACKET_SIZE_EMPTY) {\r
- _global_stats._nraw_send[PACKET_SIZE_EMPTY_BUCKET]++;\r
- } else if (length <= PACKET_SIZE_SMALL) {\r
- _global_stats._nraw_send[PACKET_SIZE_SMALL_BUCKET]++;\r
- } else\r
- _global_stats._nraw_send[PACKET_SIZE_MID_BUCKET]++;\r
- } else {\r
- if (length <= PACKET_SIZE_BIG) {\r
- _global_stats._nraw_send[PACKET_SIZE_BIG_BUCKET]++;\r
- } else\r
- _global_stats._nraw_send[PACKET_SIZE_HUGE_BUCKET]++;\r
- }\r
-}\r
-\r
-void send_to_addr(SendToProc *send_to_proc, void *send_to_userdata, const byte *p, size_t len, const PackedSockAddr &addr)\r
-{\r
- socklen_t tolen;\r
- SOCKADDR_STORAGE to = addr.get_sockaddr_storage(&tolen);\r
- UTP_RegisterSentPacket(len);\r
- send_to_proc(send_to_userdata, p, len, (const struct sockaddr *)&to, tolen);\r
-}\r
-\r
-void UTPSocket::send_data(PacketFormat* b, size_t length, bandwidth_type_t type)\r
-{\r
- // time stamp this packet with local time, the stamp goes into\r
- // the header of every packet at the 8th byte for 8 bytes :\r
- // two integers, check packet.h for more\r
- uint64 time = UTP_GetMicroseconds();\r
-\r
- PacketFormatV1* b1 = (PacketFormatV1*)b;\r
- if (version == 0) {\r
- b->tv_sec = (uint32)(time / 1000000);\r
- b->tv_usec = time % 1000000;\r
- b->reply_micro = reply_micro;\r
- } else {\r
- b1->tv_usec = (uint32)time;\r
- b1->reply_micro = reply_micro;\r
- }\r
-\r
- last_sent_packet = g_current_ms;\r
-\r
-#ifdef _DEBUG\r
- _stats._nbytes_xmit += length;\r
- ++_stats._nxmit;\r
-#endif\r
- if (userdata) {\r
- size_t n;\r
- if (type == payload_bandwidth) {\r
- // if this packet carries payload, just\r
- // count the header as overhead\r
- type = header_overhead;\r
- n = get_overhead();\r
- } else {\r
- n = length + get_udp_overhead();\r
- }\r
- func.on_overhead(userdata, true, n, type);\r
- }\r
-#if g_log_utp_verbose\r
- int flags = version == 0 ? b->flags : b1->type();\r
- uint16 seq_nr = version == 0 ? b->seq_nr : b1->seq_nr;\r
- uint16 ack_nr = version == 0 ? b->ack_nr : b1->ack_nr;\r
- LOG_UTPV("0x%08x: send %s len:%u id:%u timestamp:"I64u" reply_micro:%u flags:%s seq_nr:%u ack_nr:%u",\r
- this, addrfmt(addr, addrbuf), (uint)length, conn_id_send, time, reply_micro, flagnames[flags],\r
- seq_nr, ack_nr);\r
-#endif\r
- send_to_addr(send_to_proc, send_to_userdata, (const byte*)b, length, addr);\r
-}\r
-\r
-void UTPSocket::send_ack(bool synack)\r
-{\r
- PacketFormatExtensions pfe;\r
- zeromem(&pfe);\r
- PacketFormatExtensionsV1& pfe1 = (PacketFormatExtensionsV1&)pfe;\r
- PacketFormatAck& pfa = (PacketFormatAck&)pfe1;\r
- PacketFormatAckV1& pfa1 = (PacketFormatAckV1&)pfe1;\r
-\r
- size_t len;\r
- last_rcv_win = get_rcv_window();\r
- if (version == 0) {\r
- pfa.pf.connid = conn_id_send;\r
- pfa.pf.ack_nr = (uint16)ack_nr;\r
- pfa.pf.seq_nr = (uint16)seq_nr;\r
- pfa.pf.flags = ST_STATE;\r
- pfa.pf.ext = 0;\r
- pfa.pf.windowsize = (byte)DIV_ROUND_UP(last_rcv_win, PACKET_SIZE);\r
- len = sizeof(PacketFormat);\r
- } else {\r
- pfa1.pf.set_version(1);\r
- pfa1.pf.set_type(ST_STATE);\r
- pfa1.pf.ext = 0;\r
- pfa1.pf.connid = conn_id_send;\r
- pfa1.pf.ack_nr = ack_nr;\r
- pfa1.pf.seq_nr = seq_nr;\r
- pfa1.pf.windowsize = (uint32)last_rcv_win;\r
- len = sizeof(PacketFormatV1);\r
- }\r
-\r
- // we never need to send EACK for connections\r
- // that are shutting down\r
- if (reorder_count != 0 && state < CS_GOT_FIN) {\r
- // if reorder count > 0, send an EACK.\r
- // reorder count should always be 0\r
- // for synacks, so this should not be\r
- // as synack\r
- assert(!synack);\r
- if (version == 0) {\r
- pfa.pf.ext = 1;\r
- pfa.ext_next = 0;\r
- pfa.ext_len = 4;\r
- } else {\r
- pfa1.pf.ext = 1;\r
- pfa1.ext_next = 0;\r
- pfa1.ext_len = 4;\r
- }\r
- uint m = 0;\r
-\r
- // reorder count should only be non-zero\r
- // if the packet ack_nr + 1 has not yet\r
- // been received\r
- assert(inbuf.get(ack_nr + 1) == NULL);\r
- size_t window = min<size_t>(14+16, inbuf.size());\r
- // Generate bit mask of segments received.\r
- for (size_t i = 0; i < window; i++) {\r
- if (inbuf.get(ack_nr + i + 2) != NULL) {\r
- m |= 1 << i;\r
- LOG_UTPV("0x%08x: EACK packet [%u]", this, ack_nr + i + 2);\r
- }\r
- }\r
- if (version == 0) {\r
- pfa.acks[0] = (byte)m;\r
- pfa.acks[1] = (byte)(m >> 8);\r
- pfa.acks[2] = (byte)(m >> 16);\r
- pfa.acks[3] = (byte)(m >> 24);\r
- } else {\r
- pfa1.acks[0] = (byte)m;\r
- pfa1.acks[1] = (byte)(m >> 8);\r
- pfa1.acks[2] = (byte)(m >> 16);\r
- pfa1.acks[3] = (byte)(m >> 24);\r
- }\r
- len += 4 + 2;\r
- LOG_UTPV("0x%08x: Sending EACK %u [%u] bits:[%032b]", this, ack_nr, conn_id_send, m);\r
- } else if (synack) {\r
- // we only send "extensions" in response to SYN\r
- // and the reorder count is 0 in that state\r
-\r
- LOG_UTPV("0x%08x: Sending ACK %u [%u] with extension bits", this, ack_nr, conn_id_send);\r
- if (version == 0) {\r
- pfe.pf.ext = 2;\r
- pfe.ext_next = 0;\r
- pfe.ext_len = 8;\r
- memset(pfe.extensions, 0, 8);\r
- } else {\r
- pfe1.pf.ext = 2;\r
- pfe1.ext_next = 0;\r
- pfe1.ext_len = 8;\r
- memset(pfe1.extensions, 0, 8);\r
- }\r
- len += 8 + 2;\r
- } else {\r
- LOG_UTPV("0x%08x: Sending ACK %u [%u]", this, ack_nr, conn_id_send);\r
- }\r
-\r
- sent_ack();\r
- send_data((PacketFormat*)&pfe, len, ack_overhead);\r
-}\r
-\r
-void UTPSocket::send_keep_alive()\r
-{\r
- ack_nr--;\r
- LOG_UTPV("0x%08x: Sending KeepAlive ACK %u [%u]", this, ack_nr, conn_id_send);\r
- send_ack();\r
- ack_nr++;\r
-}\r
-\r
-void UTPSocket::send_rst(SendToProc *send_to_proc, void *send_to_userdata,\r
- const PackedSockAddr &addr, uint32 conn_id_send, uint16 ack_nr, uint16 seq_nr, byte version)\r
-{\r
- PacketFormat pf;\r
- zeromem(&pf);\r
- PacketFormatV1& pf1 = (PacketFormatV1&)pf;\r
-\r
- size_t len;\r
- if (version == 0) {\r
- pf.connid = conn_id_send;\r
- pf.ack_nr = ack_nr;\r
- pf.seq_nr = seq_nr;\r
- pf.flags = ST_RESET;\r
- pf.ext = 0;\r
- pf.windowsize = 0;\r
- len = sizeof(PacketFormat);\r
- } else {\r
- pf1.set_version(1);\r
- pf1.set_type(ST_RESET);\r
- pf1.ext = 0;\r
- pf1.connid = conn_id_send;\r
- pf1.ack_nr = ack_nr;\r
- pf1.seq_nr = seq_nr;\r
- pf1.windowsize = 0;\r
- len = sizeof(PacketFormatV1);\r
- }\r
-\r
- LOG_UTPV("%s: Sending RST id:%u seq_nr:%u ack_nr:%u", addrfmt(addr, addrbuf), conn_id_send, seq_nr, ack_nr);\r
- LOG_UTPV("send %s len:%u id:%u", addrfmt(addr, addrbuf), (uint)len, conn_id_send);\r
- send_to_addr(send_to_proc, send_to_userdata, (const byte*)&pf1, len, addr);\r
-}\r
-\r
-void UTPSocket::send_packet(OutgoingPacket *pkt)\r
-{\r
- // only count against the quota the first time we\r
- // send the packet. Don't enforce quota when closing\r
- // a socket. Only enforce the quota when we're sending\r
- // at slow rates (max window < packet size)\r
- size_t max_send = min(max_window, opt_sndbuf, max_window_user);\r
-\r
- if (pkt->transmissions == 0 || pkt->need_resend) {\r
- cur_window += pkt->payload;\r
- }\r
-\r
- size_t packet_size = get_packet_size();\r
- if (pkt->transmissions == 0 && max_send < packet_size) {\r
- assert(state == CS_FIN_SENT ||\r
- (int32)pkt->payload <= send_quota / 100);\r
- send_quota = send_quota - (int32)(pkt->payload * 100);\r
- }\r
-\r
- pkt->need_resend = false;\r
-\r
- PacketFormatV1* p1 = (PacketFormatV1*)pkt->data;\r
- PacketFormat* p = (PacketFormat*)pkt->data;\r
- if (version == 0) {\r
- p->ack_nr = ack_nr;\r
- } else {\r
- p1->ack_nr = ack_nr;\r
- }\r
- pkt->time_sent = UTP_GetMicroseconds();\r
- pkt->transmissions++;\r
- sent_ack();\r
- send_data((PacketFormat*)pkt->data, pkt->length,\r
- (state == CS_SYN_SENT) ? connect_overhead\r
- : (pkt->transmissions == 1) ? payload_bandwidth\r
- : retransmit_overhead);\r
-}\r
-\r
-bool UTPSocket::is_writable(size_t to_write)\r
-{\r
- // return true if it's OK to stuff another packet into the\r
- // outgoing queue. Since we may be using packet pacing, we\r
- // might not actually send the packet right away to affect the\r
- // cur_window. The only thing that happens when we add another\r
- // packet is that cur_window_packets is increased.\r
- size_t max_send = min(max_window, opt_sndbuf, max_window_user);\r
-\r
- size_t packet_size = get_packet_size();\r
-\r
- if (cur_window + packet_size >= max_window)\r
- last_maxed_out_window = g_current_ms;\r
-\r
- // if we don't have enough quota, we can't write regardless\r
- if (USE_PACKET_PACING) {\r
- if (send_quota / 100 < (int32)to_write) return false;\r
- }\r
-\r
- // subtract one to save space for the FIN packet\r
- if (cur_window_packets >= OUTGOING_BUFFER_MAX_SIZE - 1) return false;\r
-\r
- // if sending another packet would not make the window exceed\r
- // the max_window, we can write\r
- if (cur_window + packet_size <= max_send) return true;\r
-\r
- // if the window size is less than a packet, and we have enough\r
- // quota to send a packet, we can write, even though it would\r
- // make the window exceed the max size\r
- // the last condition is needed to not put too many packets\r
- // in the send buffer. cur_window isn't updated until we flush\r
- // the send buffer, so we need to take the number of packets\r
- // into account\r
- if (USE_PACKET_PACING) {\r
- if (max_window < to_write &&\r
- cur_window < max_window &&\r
- cur_window_packets == 0) {\r
- return true;\r
- }\r
- }\r
-\r
- return false;\r
-}\r
-\r
-bool UTPSocket::flush_packets()\r
-{\r
- size_t packet_size = get_packet_size();\r
-\r
- // send packets that are waiting on the pacer to be sent\r
- // i has to be an unsigned 16 bit counter to wrap correctly\r
- // signed types are not guaranteed to wrap the way you expect\r
- for (uint16 i = seq_nr - cur_window_packets; i != seq_nr; ++i) {\r
- OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(i);\r
- if (pkt == 0 || (pkt->transmissions > 0 && pkt->need_resend == false)) continue;\r
- // have we run out of quota?\r
- if (!is_writable(pkt->payload)) {\r
- return true;\r
- }\r
-\r
- // Nagle check\r
- // don't send the last packet if we have one packet in-flight\r
- // and the current packet is still smaller than packet_size.\r
- if (i != ((seq_nr - 1) & ACK_NR_MASK) ||\r
- cur_window_packets == 1 ||\r
- pkt->payload >= packet_size) {\r
- send_packet(pkt);\r
-\r
- // No need to send another ack if there is nothing to reorder.\r
- if (reorder_count == 0) {\r
- sent_ack();\r
- }\r
- }\r
- }\r
- return false;\r
-}\r
-\r
-void UTPSocket::write_outgoing_packet(size_t payload, uint flags)\r
-{\r
- // Setup initial timeout timer\r
- if (cur_window_packets == 0) {\r
- retransmit_timeout = rto;\r
- rto_timeout = g_current_ms + retransmit_timeout;\r
- assert(cur_window == 0);\r
- }\r
-\r
- size_t packet_size = get_packet_size();\r
- do {\r
- assert(cur_window_packets < OUTGOING_BUFFER_MAX_SIZE);\r
- assert(flags == ST_DATA || flags == ST_FIN);\r
-\r
- size_t added = 0;\r
-\r
- OutgoingPacket *pkt = NULL;\r
- \r
- if (cur_window_packets > 0) {\r
- pkt = (OutgoingPacket*)outbuf.get(seq_nr - 1);\r
- }\r
-\r
- const size_t header_size = get_header_size();\r
- bool append = true;\r
-\r
- // if there's any room left in the last packet in the window\r
- // and it hasn't been sent yet, fill that frame first\r
- if (payload && pkt && !pkt->transmissions && pkt->payload < packet_size) {\r
- // Use the previous unsent packet\r
- added = min(payload + pkt->payload, max<size_t>(packet_size, pkt->payload)) - pkt->payload;\r
- pkt = (OutgoingPacket*)realloc(pkt,\r
- (sizeof(OutgoingPacket) - 1) +\r
- header_size +\r
- pkt->payload + added);\r
- outbuf.put(seq_nr - 1, pkt);\r
- append = false;\r
- assert(!pkt->need_resend);\r
- } else {\r
- // Create the packet to send.\r
- added = payload;\r
- pkt = (OutgoingPacket*)malloc((sizeof(OutgoingPacket) - 1) +\r
- header_size +\r
- added);\r
- pkt->payload = 0;\r
- pkt->transmissions = 0;\r
- pkt->need_resend = false;\r
- }\r
-\r
- if (added) {\r
- // Fill it with data from the upper layer.\r
- func.on_write(userdata, pkt->data + header_size + pkt->payload, added);\r
- }\r
- pkt->payload += added;\r
- pkt->length = header_size + pkt->payload;\r
-\r
- last_rcv_win = get_rcv_window();\r
-\r
- PacketFormat* p = (PacketFormat*)pkt->data;\r
- PacketFormatV1* p1 = (PacketFormatV1*)pkt->data;\r
- if (version == 0) {\r
- p->connid = conn_id_send;\r
- p->ext = 0;\r
- p->windowsize = (byte)DIV_ROUND_UP(last_rcv_win, PACKET_SIZE);\r
- p->ack_nr = ack_nr;\r
- p->flags = flags;\r
- } else {\r
- p1->set_version(1);\r
- p1->set_type(flags);\r
- p1->ext = 0;\r
- p1->connid = conn_id_send;\r
- p1->windowsize = (uint32)last_rcv_win;\r
- p1->ack_nr = ack_nr;\r
- }\r
-\r
- if (append) {\r
- // Remember the message in the outgoing queue.\r
- outbuf.ensure_size(seq_nr, cur_window_packets);\r
- outbuf.put(seq_nr, pkt);\r
- if (version == 0) p->seq_nr = seq_nr;\r
- else p1->seq_nr = seq_nr;\r
- seq_nr++;\r
- cur_window_packets++;\r
- }\r
-\r
- payload -= added;\r
-\r
- } while (payload);\r
-\r
- flush_packets();\r
-}\r
-\r
-void UTPSocket::update_send_quota()\r
-{\r
- int dt = g_current_ms - last_send_quota;\r
- if (dt == 0) return;\r
- last_send_quota = g_current_ms;\r
- size_t add = max_window * dt * 100 / (rtt_hist.delay_base?rtt_hist.delay_base:50);\r
- if (add > max_window * 100 && add > MAX_CWND_INCREASE_BYTES_PER_RTT * 100) add = max_window;\r
- send_quota += (int32)add;\r
-// LOG_UTPV("0x%08x: UTPSocket::update_send_quota dt:%d rtt:%u max_window:%u quota:%d",\r
-// this, dt, rtt, (uint)max_window, send_quota / 100);\r
-}\r
-\r
-#ifdef _DEBUG\r
-void UTPSocket::check_invariant()\r
-{\r
- if (reorder_count > 0) {\r
- assert(inbuf.get(ack_nr + 1) == NULL);\r
- }\r
-\r
- size_t outstanding_bytes = 0;\r
- for (int i = 0; i < cur_window_packets; ++i) {\r
- OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(seq_nr - i - 1);\r
- if (pkt == 0 || pkt->transmissions == 0 || pkt->need_resend) continue;\r
- outstanding_bytes += pkt->payload;\r
- }\r
- assert(outstanding_bytes == cur_window);\r
-}\r
-#endif\r
-\r
-void UTPSocket::check_timeouts()\r
-{\r
-#ifdef _DEBUG\r
- check_invariant();\r
-#endif\r
-\r
- // this invariant should always be true\r
- assert(cur_window_packets == 0 || outbuf.get(seq_nr - cur_window_packets));\r
-\r
- LOG_UTPV("0x%08x: CheckTimeouts timeout:%d max_window:%u cur_window:%u quota:%d "\r
- "state:%s cur_window_packets:%u bytes_since_ack:%u ack_time:%d",\r
- this, (int)(rto_timeout - g_current_ms), (uint)max_window, (uint)cur_window,\r
- send_quota / 100, statenames[state], cur_window_packets,\r
- (uint)bytes_since_ack, (int)(g_current_ms - ack_time));\r
-\r
- update_send_quota();\r
- flush_packets();\r
-\r
-\r
- if (USE_PACKET_PACING) {\r
- // In case the new send quota made it possible to send another packet\r
- // Mark the socket as writable. If we don't use pacing, the send\r
- // quota does not affect if the socket is writeable\r
- // if we don't use packet pacing, the writable event is triggered\r
- // whenever the cur_window falls below the max_window, so we don't\r
- // need this check then\r
- if (state == CS_CONNECTED_FULL && is_writable(get_packet_size())) {\r
- state = CS_CONNECTED;\r
- LOG_UTPV("0x%08x: Socket writable. max_window:%u cur_window:%u quota:%d packet_size:%u",\r
- this, (uint)max_window, (uint)cur_window, send_quota / 100, (uint)get_packet_size());\r
- func.on_state(userdata, UTP_STATE_WRITABLE);\r
- }\r
- }\r
-\r
- switch (state) {\r
- case CS_SYN_SENT:\r
- case CS_CONNECTED_FULL:\r
- case CS_CONNECTED:\r
- case CS_FIN_SENT: {\r
-\r
- // Reset max window...\r
- if ((int)(g_current_ms - zerowindow_time) >= 0 && max_window_user == 0) {\r
- max_window_user = PACKET_SIZE;\r
- }\r
-\r
- if ((int)(g_current_ms - rto_timeout) >= 0 &&\r
- (!(USE_PACKET_PACING) || cur_window_packets > 0) &&\r
- rto_timeout > 0) {\r
-\r
- /*\r
- OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(seq_nr - cur_window_packets);\r
- \r
- // If there were a lot of retransmissions, force recomputation of round trip time\r
- if (pkt->transmissions >= 4)\r
- rtt = 0;\r
- */\r
-\r
- // Increase RTO\r
- const uint new_timeout = retransmit_timeout * 2;\r
- if (new_timeout >= 30000 || (state == CS_SYN_SENT && new_timeout > 6000)) {\r
- // more than 30 seconds with no reply. kill it.\r
- // if we haven't even connected yet, give up sooner. 6 seconds\r
- // means 2 tries at the following timeouts: 3, 6 seconds\r
- if (state == CS_FIN_SENT)\r
- state = CS_DESTROY;\r
- else\r
- state = CS_RESET;\r
- func.on_error(userdata, ETIMEDOUT);\r
- goto getout;\r
- }\r
-\r
- retransmit_timeout = new_timeout;\r
- rto_timeout = g_current_ms + new_timeout;\r
-\r
- // On Timeout\r
- duplicate_ack = 0;\r
-\r
- // rate = min_rate\r
- max_window = get_packet_size();\r
- send_quota = max<int32>((int32)max_window * 100, send_quota);\r
-\r
- // every packet should be considered lost\r
- for (int i = 0; i < cur_window_packets; ++i) {\r
- OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(seq_nr - i - 1);\r
- if (pkt == 0 || pkt->transmissions == 0 || pkt->need_resend) continue;\r
- pkt->need_resend = true;\r
- assert(cur_window >= pkt->payload);\r
- cur_window -= pkt->payload;\r
- }\r
-\r
- // used in parse_log.py\r
- LOG_UTP("0x%08x: Packet timeout. Resend. seq_nr:%u. timeout:%u max_window:%u",\r
- this, seq_nr - cur_window_packets, retransmit_timeout, (uint)max_window);\r
-\r
- fast_timeout = true;\r
- timeout_seq_nr = seq_nr;\r
-\r
- if (cur_window_packets > 0) {\r
- OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(seq_nr - cur_window_packets);\r
- assert(pkt);\r
- send_quota = max<int32>((int32)pkt->length * 100, send_quota);\r
-\r
- // Re-send the packet.\r
- send_packet(pkt);\r
- }\r
- }\r
-\r
- // Mark the socket as writable\r
- if (state == CS_CONNECTED_FULL && is_writable(get_packet_size())) {\r
- state = CS_CONNECTED;\r
- LOG_UTPV("0x%08x: Socket writable. max_window:%u cur_window:%u quota:%d packet_size:%u",\r
- this, (uint)max_window, (uint)cur_window, send_quota / 100, (uint)get_packet_size());\r
- func.on_state(userdata, UTP_STATE_WRITABLE);\r
- }\r
-\r
- if (state >= CS_CONNECTED && state <= CS_FIN_SENT) {\r
- // Send acknowledgment packets periodically, or when the threshold is reached\r
- if (bytes_since_ack > DELAYED_ACK_BYTE_THRESHOLD ||\r
- (int)(g_current_ms - ack_time) >= 0) {\r
- send_ack();\r
- }\r
-\r
- if ((int)(g_current_ms - last_sent_packet) >= KEEPALIVE_INTERVAL) {\r
- send_keep_alive();\r
- }\r
- }\r
-\r
- break;\r
- }\r
-\r
- // Close?\r
- case CS_GOT_FIN:\r
- case CS_DESTROY_DELAY:\r
- if ((int)(g_current_ms - rto_timeout) >= 0) {\r
- state = (state == CS_DESTROY_DELAY) ? CS_DESTROY : CS_RESET;\r
- if (cur_window_packets > 0 && userdata) {\r
- func.on_error(userdata, ECONNRESET);\r
- }\r
- }\r
- break;\r
- // prevent warning\r
- case CS_IDLE:\r
- case CS_RESET:\r
- case CS_DESTROY:\r
- break;\r
- }\r
-\r
- getout:\r
-\r
- // make sure we don't accumulate quota when we don't have\r
- // anything to send\r
- int32 limit = max<int32>((int32)max_window / 2, 5 * (int32)get_packet_size()) * 100;\r
- if (send_quota > limit) send_quota = limit;\r
-}\r
-\r
-// returns:\r
-// 0: the packet was acked.\r
-// 1: it means that the packet had already been acked\r
-// 2: the packet has not been sent yet\r
-int UTPSocket::ack_packet(uint16 seq)\r
-{\r
- OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(seq);\r
-\r
- // the packet has already been acked (or not sent)\r
- if (pkt == NULL) {\r
- LOG_UTPV("0x%08x: got ack for:%u (already acked, or never sent)", this, seq);\r
- return 1;\r
- }\r
-\r
- // can't ack packets that haven't been sent yet!\r
- if (pkt->transmissions == 0) {\r
- LOG_UTPV("0x%08x: got ack for:%u (never sent, pkt_size:%u need_resend:%u)",\r
- this, seq, (uint)pkt->payload, pkt->need_resend);\r
- return 2;\r
- }\r
-\r
- LOG_UTPV("0x%08x: got ack for:%u (pkt_size:%u need_resend:%u)",\r
- this, seq, (uint)pkt->payload, pkt->need_resend);\r
-\r
- outbuf.put(seq, NULL);\r
-\r
- // if we never re-sent the packet, update the RTT estimate\r
- if (pkt->transmissions == 1) {\r
- // Estimate the round trip time.\r
- const uint32 ertt = (uint32)((UTP_GetMicroseconds() - pkt->time_sent) / 1000);\r
- if (rtt == 0) {\r
- // First round trip time sample\r
- rtt = ertt;\r
- rtt_var = ertt / 2;\r
- // sanity check. rtt should never be more than 6 seconds\r
-// assert(rtt < 6000);\r
- } else {\r
- // Compute new round trip times\r
- const int delta = (int)rtt - ertt;\r
- rtt_var = rtt_var + (int)(abs(delta) - rtt_var) / 4;\r
- rtt = rtt - rtt/8 + ertt/8;\r
- // sanity check. rtt should never be more than 6 seconds\r
-// assert(rtt < 6000);\r
- rtt_hist.add_sample(ertt);\r
- }\r
- rto = max<uint>(rtt + rtt_var * 4, 500);\r
- LOG_UTPV("0x%08x: rtt:%u avg:%u var:%u rto:%u",\r
- this, ertt, rtt, rtt_var, rto);\r
- }\r
- retransmit_timeout = rto;\r
- rto_timeout = g_current_ms + rto;\r
- // if need_resend is set, this packet has already\r
- // been considered timed-out, and is not included in\r
- // the cur_window anymore\r
- if (!pkt->need_resend) {\r
- assert(cur_window >= pkt->payload);\r
- cur_window -= pkt->payload;\r
- }\r
- free(pkt);\r
- return 0;\r
-}\r
-\r
-// count the number of bytes that were acked by the EACK header\r
-size_t UTPSocket::selective_ack_bytes(uint base, const byte* mask, byte len, int64& min_rtt)\r
-{\r
- if (cur_window_packets == 0) return 0;\r
-\r
- size_t acked_bytes = 0;\r
- int bits = len * 8;\r
-\r
- do {\r
- uint v = base + bits;\r
-\r
- // ignore bits that haven't been sent yet\r
- // see comment in UTPSocket::selective_ack\r
- if (((seq_nr - v - 1) & ACK_NR_MASK) >= (uint16)(cur_window_packets - 1))\r
- continue;\r
-\r
- // ignore bits that represents packets we haven't sent yet\r
- // or packets that have already been acked\r
- OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(v);\r
- if (!pkt || pkt->transmissions == 0)\r
- continue;\r
-\r
- // Count the number of segments that were successfully received past it.\r
- if (bits >= 0 && mask[bits>>3] & (1 << (bits & 7))) {\r
- assert((int)(pkt->payload) >= 0);\r
- acked_bytes += pkt->payload;\r
- min_rtt = min<int64>(min_rtt, UTP_GetMicroseconds() - pkt->time_sent);\r
- continue;\r
- }\r
- } while (--bits >= -1);\r
- return acked_bytes;\r
-}\r
-\r
-enum { MAX_EACK = 128 };\r
-\r
-void UTPSocket::selective_ack(uint base, const byte *mask, byte len)\r
-{\r
- if (cur_window_packets == 0) return;\r
-\r
- // the range is inclusive [0, 31] bits\r
- int bits = len * 8 - 1;\r
-\r
- int count = 0;\r
-\r
- // resends is a stack of sequence numbers we need to resend. Since we\r
- // iterate in reverse over the acked packets, at the end, the top packets\r
- // are the ones we want to resend\r
- int resends[MAX_EACK];\r
- int nr = 0;\r
-\r
- LOG_UTPV("0x%08x: Got EACK [%032b] base:%u", this, *(uint32*)mask, base);\r
- do {\r
- // we're iterating over the bits from higher sequence numbers\r
- // to lower (kind of in reverse order, wich might not be very\r
- // intuitive)\r
- uint v = base + bits;\r
-\r
- // ignore bits that haven't been sent yet\r
- // and bits that fall below the ACKed sequence number\r
- // this can happen if an EACK message gets\r
- // reordered and arrives after a packet that ACKs up past\r
- // the base for thie EACK message\r
-\r
- // this is essentially the same as:\r
- // if v >= seq_nr || v <= seq_nr - cur_window_packets\r
- // but it takes wrapping into account\r
-\r
- // if v == seq_nr the -1 will make it wrap. if v > seq_nr\r
- // it will also wrap (since it will fall further below 0)\r
- // and be > cur_window_packets.\r
- // if v == seq_nr - cur_window_packets, the result will be\r
- // seq_nr - (seq_nr - cur_window_packets) - 1\r
- // == seq_nr - seq_nr + cur_window_packets - 1\r
- // == cur_window_packets - 1 which will be caught by the\r
- // test. If v < seq_nr - cur_window_packets the result will grow\r
- // fall furhter outside of the cur_window_packets range.\r
-\r
- // sequence number space:\r
- //\r
- // rejected < accepted > rejected \r
- // <============+--------------+============>\r
- // ^ ^\r
- // | |\r
- // (seq_nr-wnd) seq_nr\r
-\r
- if (((seq_nr - v - 1) & ACK_NR_MASK) >= (uint16)(cur_window_packets - 1))\r
- continue;\r
-\r
- // this counts as a duplicate ack, even though we might have\r
- // received an ack for this packet previously (in another EACK\r
- // message for instance)\r
- bool bit_set = bits >= 0 && mask[bits>>3] & (1 << (bits & 7));\r
-\r
- // if this packet is acked, it counts towards the duplicate ack counter\r
- if (bit_set) count++;\r
-\r
- // ignore bits that represents packets we haven't sent yet\r
- // or packets that have already been acked\r
- OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(v);\r
- if (!pkt || pkt->transmissions == 0) {\r
- LOG_UTPV("0x%08x: skipping %u. pkt:%08x transmissions:%u %s",\r
- this, v, pkt, pkt?pkt->transmissions:0, pkt?"(not sent yet?)":"(already acked?)");\r
- continue;\r
- }\r
-\r
- // Count the number of segments that were successfully received past it.\r
- if (bit_set) {\r
- // the selective ack should never ACK the packet we're waiting for to decrement cur_window_packets\r
- assert((v & outbuf.mask) != ((seq_nr - cur_window_packets) & outbuf.mask));\r
- ack_packet(v);\r
- continue;\r
- }\r
-\r
- // Resend segments\r
- // if count is less than our re-send limit, we haven't seen enough\r
- // acked packets in front of this one to warrant a re-send.\r
- // if count == 0, we're still going through the tail of zeroes\r
- if (((v - fast_resend_seq_nr) & ACK_NR_MASK) <= OUTGOING_BUFFER_MAX_SIZE &&\r
- count >= DUPLICATE_ACKS_BEFORE_RESEND &&\r
- duplicate_ack < DUPLICATE_ACKS_BEFORE_RESEND) {\r
- // resends is a stack, and we're mostly interested in the top of it\r
- // if we're full, just throw away the lower half\r
- if (nr >= MAX_EACK - 2) {\r
- memmove(resends, &resends[MAX_EACK/2], MAX_EACK/2 * sizeof(resends[0]));\r
- nr -= MAX_EACK / 2;\r
- }\r
- resends[nr++] = v;\r
- LOG_UTPV("0x%08x: no ack for %u", this, v);\r
- } else {\r
- LOG_UTPV("0x%08x: not resending %u count:%d dup_ack:%u fast_resend_seq_nr:%u",\r
- this, v, count, duplicate_ack, fast_resend_seq_nr);\r
- }\r
- } while (--bits >= -1);\r
-\r
- if (((base - 1 - fast_resend_seq_nr) & ACK_NR_MASK) <= OUTGOING_BUFFER_MAX_SIZE &&\r
- count >= DUPLICATE_ACKS_BEFORE_RESEND) {\r
- // if we get enough duplicate acks to start\r
- // resending, the first packet we should resend\r
- // is base-1\r
- resends[nr++] = (base - 1) & ACK_NR_MASK;\r
- } else {\r
- LOG_UTPV("0x%08x: not resending %u count:%d dup_ack:%u fast_resend_seq_nr:%u",\r
- this, base - 1, count, duplicate_ack, fast_resend_seq_nr);\r
- }\r
-\r
- bool back_off = false;\r
- int i = 0;\r
- while (nr > 0) {\r
- uint v = resends[--nr];\r
- // don't consider the tail of 0:es to be lost packets\r
- // only unacked packets with acked packets after should\r
- // be considered lost\r
- OutgoingPacket *pkt = (OutgoingPacket*)outbuf.get(v);\r
-\r
- // this may be an old (re-ordered) packet, and some of the\r
- // packets in here may have been acked already. In which\r
- // case they will not be in the send queue anymore\r
- if (!pkt) continue;\r
-\r
- // used in parse_log.py\r
- LOG_UTP("0x%08x: Packet %u lost. Resending", this, v);\r
-\r
- // On Loss\r
- back_off = true;\r
-#ifdef _DEBUG\r
- ++_stats._rexmit;\r
-#endif\r
- send_packet(pkt);\r
- fast_resend_seq_nr = v + 1;\r
-\r
- // Re-send max 4 packets.\r
- if (++i >= 4) break;\r
- }\r
-\r
- if (back_off)\r
- maybe_decay_win();\r
-\r
- duplicate_ack = count;\r
-}\r
-\r
-void UTPSocket::apply_ledbat_ccontrol(size_t bytes_acked, uint32 actual_delay, int64 min_rtt)\r
-{\r
- // the delay can never be greater than the rtt. The min_rtt\r
- // variable is the RTT in microseconds\r
- \r
- assert(min_rtt >= 0);\r
- int32 our_delay = min<uint32>(our_hist.get_value(), uint32(min_rtt));\r
- assert(our_delay != INT_MAX);\r
- assert(our_delay >= 0);\r
-\r
- SOCKADDR_STORAGE sa = addr.get_sockaddr_storage();\r
- UTP_DelaySample((sockaddr*)&sa, our_delay / 1000);\r
-\r
- // This test the connection under heavy load from foreground\r
- // traffic. Pretend that our delays are very high to force the\r
- // connection to use sub-packet size window sizes\r
- //our_delay *= 4;\r
-\r
- // target is microseconds\r
- int target = CCONTROL_TARGET;\r
- if (target <= 0) target = 100000;\r
-\r
- double off_target = target - our_delay;\r
-\r
- // this is the same as:\r
- //\r
- // (min(off_target, target) / target) * (bytes_acked / max_window) * MAX_CWND_INCREASE_BYTES_PER_RTT\r
- //\r
- // so, it's scaling the max increase by the fraction of the window this ack represents, and the fraction\r
- // of the target delay the current delay represents.\r
- // The min() around off_target protects against crazy values of our_delay, which may happen when th\r
- // timestamps wraps, or by just having a malicious peer sending garbage. This caps the increase\r
- // of the window size to MAX_CWND_INCREASE_BYTES_PER_RTT per rtt.\r
- // as for large negative numbers, this direction is already capped at the min packet size further down\r
- // the min around the bytes_acked protects against the case where the window size was recently\r
- // shrunk and the number of acked bytes exceeds that. This is considered no more than one full\r
- // window, in order to keep the gain within sane boundries.\r
-\r
- assert(bytes_acked > 0);\r
- double window_factor = (double)min(bytes_acked, max_window) / (double)max(max_window, bytes_acked);\r
- double delay_factor = off_target / target;\r
- double scaled_gain = MAX_CWND_INCREASE_BYTES_PER_RTT * window_factor * delay_factor;\r
-\r
- // since MAX_CWND_INCREASE_BYTES_PER_RTT is a cap on how much the window size (max_window)\r
- // may increase per RTT, we may not increase the window size more than that proportional\r
- // to the number of bytes that were acked, so that once one window has been acked (one rtt)\r
- // the increase limit is not exceeded\r
- // the +1. is to allow for floating point imprecision\r
- assert(scaled_gain <= 1. + MAX_CWND_INCREASE_BYTES_PER_RTT * (int)min(bytes_acked, max_window) / (double)max(max_window, bytes_acked));\r
-\r
- if (scaled_gain > 0 && g_current_ms - last_maxed_out_window > 300) {\r
- // if it was more than 300 milliseconds since we tried to send a packet\r
- // and stopped because we hit the max window, we're most likely rate\r
- // limited (which prevents us from ever hitting the window size)\r
- // if this is the case, we cannot let the max_window grow indefinitely\r
- scaled_gain = 0;\r
- }\r
-\r
- if (scaled_gain + max_window < MIN_WINDOW_SIZE) {\r
- max_window = MIN_WINDOW_SIZE;\r
- } else {\r
- max_window = (size_t)(max_window + scaled_gain);\r
- }\r
-\r
- // make sure that the congestion window is below max\r
- // make sure that we don't shrink our window too small\r
- max_window = clamp<size_t>(max_window, MIN_WINDOW_SIZE, opt_sndbuf);\r
-\r
- // used in parse_log.py\r
- LOG_UTP("0x%08x: actual_delay:%u our_delay:%d their_delay:%u off_target:%d max_window:%u "\r
- "delay_base:%u delay_sum:%d target_delay:%d acked_bytes:%u cur_window:%u "\r
- "scaled_gain:%f rtt:%u rate:%u quota:%d wnduser:%u rto:%u timeout:%d get_microseconds:"I64u" "\r
- "cur_window_packets:%u packet_size:%u their_delay_base:%u their_actual_delay:%u",\r
- this, actual_delay, our_delay / 1000, their_hist.get_value() / 1000,\r
- (int)off_target / 1000, (uint)(max_window), our_hist.delay_base,\r
- (our_delay + their_hist.get_value()) / 1000, target / 1000, (uint)bytes_acked,\r
- (uint)(cur_window - bytes_acked), (float)(scaled_gain), rtt,\r
- (uint)(max_window * 1000 / (rtt_hist.delay_base?rtt_hist.delay_base:50)),\r
- send_quota / 100, (uint)max_window_user, rto, (int)(rto_timeout - g_current_ms),\r
- UTP_GetMicroseconds(), cur_window_packets, (uint)get_packet_size(),\r
- their_hist.delay_base, their_hist.delay_base + their_hist.get_value());\r
-}\r
-\r
-static void UTP_RegisterRecvPacket(UTPSocket *conn, size_t len)\r
-{\r
-#ifdef _DEBUG\r
- ++conn->_stats._nrecv;\r
- conn->_stats._nbytes_recv += len;\r
-#endif\r
-\r
- if (len <= PACKET_SIZE_MID) {\r
- if (len <= PACKET_SIZE_EMPTY) {\r
- _global_stats._nraw_recv[PACKET_SIZE_EMPTY_BUCKET]++;\r
- } else if (len <= PACKET_SIZE_SMALL) {\r
- _global_stats._nraw_recv[PACKET_SIZE_SMALL_BUCKET]++;\r
- } else \r
- _global_stats._nraw_recv[PACKET_SIZE_MID_BUCKET]++;\r
- } else {\r
- if (len <= PACKET_SIZE_BIG) {\r
- _global_stats._nraw_recv[PACKET_SIZE_BIG_BUCKET]++;\r
- } else \r
- _global_stats._nraw_recv[PACKET_SIZE_HUGE_BUCKET]++;\r
- }\r
-}\r
-\r
-// returns the max number of bytes of payload the uTP\r
-// connection is allowed to send\r
-size_t UTPSocket::get_packet_size()\r
-{\r
- int header_size = version == 1\r
- ? sizeof(PacketFormatV1)\r
- : sizeof(PacketFormat);\r
-\r
- size_t mtu = get_udp_mtu();\r
-\r
- if (DYNAMIC_PACKET_SIZE_ENABLED) {\r
- SOCKADDR_STORAGE sa = addr.get_sockaddr_storage();\r
- size_t max_packet_size = UTP_GetPacketSize((sockaddr*)&sa);\r
- return min(mtu - header_size, max_packet_size);\r
- }\r
- else\r
- {\r
- return mtu - header_size;\r
- }\r
-}\r
-\r
-// Process an incoming packet\r
-// syn is true if this is the first packet received. It will cut off parsing\r
-// as soon as the header is done\r
-size_t UTP_ProcessIncoming(UTPSocket *conn, const byte *packet, size_t len, bool syn = false)\r
-{\r
- UTP_RegisterRecvPacket(conn, len);\r
-\r
- g_current_ms = UTP_GetMilliseconds();\r
-\r
- conn->update_send_quota();\r
-\r
- const PacketFormat *pf = (PacketFormat*)packet;\r
- const PacketFormatV1 *pf1 = (PacketFormatV1*)packet;\r
- const byte *packet_end = packet + len;\r
-\r
- uint16 pk_seq_nr;\r
- uint16 pk_ack_nr;\r
- uint8 pk_flags;\r
- if (conn->version == 0) {\r
- pk_seq_nr = pf->seq_nr;\r
- pk_ack_nr = pf->ack_nr;\r
- pk_flags = pf->flags;\r
- } else {\r
- pk_seq_nr = pf1->seq_nr;\r
- pk_ack_nr = pf1->ack_nr;\r
- pk_flags = pf1->type();\r
- }\r
-\r
- if (pk_flags >= ST_NUM_STATES) return 0;\r
-\r
- LOG_UTPV("0x%08x: Got %s. seq_nr:%u ack_nr:%u state:%s version:%u timestamp:"I64u" reply_micro:%u",\r
- conn, flagnames[pk_flags], pk_seq_nr, pk_ack_nr, statenames[conn->state], conn->version,\r
- conn->version == 0?(uint64)(pf->tv_sec) * 1000000 + pf->tv_usec:uint64(pf1->tv_usec),\r
- conn->version == 0?(uint32)(pf->reply_micro):(uint32)(pf1->reply_micro));\r
-\r
- // mark receipt time\r
- uint64 time = UTP_GetMicroseconds();\r
-\r
- // RSTs are handled earlier, since the connid matches the send id not the recv id\r
- assert(pk_flags != ST_RESET);\r
-\r
- // TODO: maybe send a ST_RESET if we're in CS_RESET?\r
-\r
- const byte *selack_ptr = NULL;\r
-\r
- // Unpack UTP packet options\r
- // Data pointer\r
- const byte *data = (const byte*)pf + conn->get_header_size();\r
- if (conn->get_header_size() > len) {\r
- LOG_UTPV("0x%08x: Invalid packet size (less than header size)", conn);\r
- return 0;\r
- }\r
- // Skip the extension headers\r
- uint extension = conn->version == 0 ? pf->ext : pf1->ext;\r
- if (extension != 0) {\r
- do {\r
- // Verify that the packet is valid.\r
- data += 2;\r
-\r
- if ((int)(packet_end - data) < 0 || (int)(packet_end - data) < data[-1]) {\r
- LOG_UTPV("0x%08x: Invalid len of extensions", conn);\r
- return 0;\r
- }\r
-\r
- switch(extension) {\r
- case 1: // Selective Acknowledgment\r
- selack_ptr = data;\r
- break;\r
- case 2: // extension bits\r
- if (data[-1] != 8) {\r
- LOG_UTPV("0x%08x: Invalid len of extension bits header", conn);\r
- return 0;\r
- }\r
- memcpy(conn->extensions, data, 8);\r
- LOG_UTPV("0x%08x: got extension bits:%02x%02x%02x%02x%02x%02x%02x%02x", conn,\r
- conn->extensions[0], conn->extensions[1], conn->extensions[2], conn->extensions[3],\r
- conn->extensions[4], conn->extensions[5], conn->extensions[6], conn->extensions[7]);\r
- }\r
- extension = data[-2];\r
- data += data[-1];\r
- } while (extension);\r
- }\r
-\r
- if (conn->state == CS_SYN_SENT) {\r
- // if this is a syn-ack, initialize our ack_nr\r
- // to match the sequence number we got from\r
- // the other end\r
- conn->ack_nr = (pk_seq_nr - 1) & SEQ_NR_MASK;\r
- }\r
-\r
- g_current_ms = UTP_GetMilliseconds();\r
- conn->last_got_packet = g_current_ms;\r
-\r
- if (syn) {\r
- return 0;\r
- }\r
-\r
- // seqnr is the number of packets past the expected\r
- // packet this is. ack_nr is the last acked, seq_nr is the\r
- // current. Subtracring 1 makes 0 mean "this is the next\r
- // expected packet".\r
- const uint seqnr = (pk_seq_nr - conn->ack_nr - 1) & SEQ_NR_MASK;\r
-\r
- // Getting an invalid sequence number?\r
- if (seqnr >= REORDER_BUFFER_MAX_SIZE) {\r
- if (seqnr >= (SEQ_NR_MASK + 1) - REORDER_BUFFER_MAX_SIZE && pk_flags != ST_STATE) {\r
- conn->ack_time = g_current_ms + min<uint>(conn->ack_time - g_current_ms, DELAYED_ACK_TIME_THRESHOLD);\r
- }\r
- LOG_UTPV(" Got old Packet/Ack (%u/%u)=%u!", pk_seq_nr, conn->ack_nr, seqnr);\r
- return 0;\r
- }\r
-\r
- // Process acknowledgment\r
- // acks is the number of packets that was acked\r
- int acks = (pk_ack_nr - (conn->seq_nr - 1 - conn->cur_window_packets)) & ACK_NR_MASK;\r
-\r
- // this happens when we receive an old ack nr\r
- if (acks > conn->cur_window_packets) acks = 0;\r
-\r
- // if we get the same ack_nr as in the last packet\r
- // increase the duplicate_ack counter, otherwise reset\r
- // it to 0\r
- if (conn->cur_window_packets > 0) {\r
- if (pk_ack_nr == ((conn->seq_nr - conn->cur_window_packets - 1) & ACK_NR_MASK) &&\r
- conn->cur_window_packets > 0) {\r
- //++conn->duplicate_ack;\r
- } else {\r
- conn->duplicate_ack = 0;\r
- }\r
-\r
- // TODO: if duplicate_ack == DUPLICATE_ACK_BEFORE_RESEND\r
- // and fast_resend_seq_nr <= ack_nr + 1\r
- // resend ack_nr + 1\r
- }\r
-\r
- // figure out how many bytes were acked\r
- size_t acked_bytes = 0;\r
-\r
- // the minimum rtt of all acks\r
- // this is the upper limit on the delay we get back\r
- // from the other peer. Our delay cannot exceed\r
- // the rtt of the packet. If it does, clamp it.\r
- // this is done in apply_ledbat_ccontrol()\r
- int64 min_rtt = INT64_MAX;\r
-\r
- for (int i = 0; i < acks; ++i) {\r
- int seq = conn->seq_nr - conn->cur_window_packets + i;\r
- OutgoingPacket *pkt = (OutgoingPacket*)conn->outbuf.get(seq);\r
- if (pkt == 0 || pkt->transmissions == 0) continue;\r
- assert((int)(pkt->payload) >= 0);\r
- acked_bytes += pkt->payload;\r
- min_rtt = min<int64>(min_rtt, UTP_GetMicroseconds() - pkt->time_sent);\r
- }\r
- \r
- // count bytes acked by EACK\r
- if (selack_ptr != NULL) {\r
- acked_bytes += conn->selective_ack_bytes((pk_ack_nr + 2) & ACK_NR_MASK,\r
- selack_ptr, selack_ptr[-1], min_rtt);\r
- }\r
-\r
- LOG_UTPV("0x%08x: acks:%d acked_bytes:%u seq_nr:%d cur_window:%u cur_window_packets:%u relative_seqnr:%u max_window:%u min_rtt:%u rtt:%u",\r
- conn, acks, (uint)acked_bytes, conn->seq_nr, (uint)conn->cur_window, conn->cur_window_packets,\r
- seqnr, (uint)conn->max_window, (uint)(min_rtt / 1000), conn->rtt);\r
-\r
- uint64 p;\r
-\r
- if (conn->version == 0) {\r
- p = uint64(pf->tv_sec) * 1000000 + pf->tv_usec;\r
- } else {\r
- p = pf1->tv_usec;\r
- }\r
-\r
- conn->last_measured_delay = g_current_ms;\r
-\r
- // get delay in both directions\r
- // record the delay to report back\r
- const uint32 their_delay = (uint32)(p == 0 ? 0 : time - p);\r
- conn->reply_micro = their_delay;\r
- uint32 prev_delay_base = conn->their_hist.delay_base;\r
- if (their_delay != 0) conn->their_hist.add_sample(their_delay);\r
-\r
- // if their new delay base is less than their previous one\r
- // we should shift our delay base in the other direction in order\r
- // to take the clock skew into account\r
- if (prev_delay_base != 0 &&\r
- wrapping_compare_less(conn->their_hist.delay_base, prev_delay_base)) {\r
- // never adjust more than 10 milliseconds\r
- if (prev_delay_base - conn->their_hist.delay_base <= 10000) {\r
- conn->our_hist.shift(prev_delay_base - conn->their_hist.delay_base);\r
- }\r
- }\r
-\r
- const uint32 actual_delay = conn->version==0\r
- ?(pf->reply_micro==INT_MAX?0:uint32(pf->reply_micro))\r
- :(uint32(pf1->reply_micro)==INT_MAX?0:uint32(pf1->reply_micro));\r
-\r
- // if the actual delay is 0, it means the other end\r
- // hasn't received a sample from us yet, and doesn't\r
- // know what it is. We can't update out history unless\r
- // we have a true measured sample\r
- prev_delay_base = conn->our_hist.delay_base;\r
- if (actual_delay != 0) conn->our_hist.add_sample(actual_delay);\r
-\r
- // if our new delay base is less than our previous one\r
- // we should shift the other end's delay base in the other\r
- // direction in order to take the clock skew into account\r
- // This is commented out because it creates bad interactions\r
- // with our adjustment in the other direction. We don't really\r
- // need our estimates of the other peer to be very accurate\r
- // anyway. The problem with shifting here is that we're more\r
- // likely shift it back later because of a low latency. This\r
- // second shift back would cause us to shift our delay base\r
- // which then get's into a death spiral of shifting delay bases\r
-/* if (prev_delay_base != 0 &&\r
- wrapping_compare_less(conn->our_hist.delay_base, prev_delay_base)) {\r
- // never adjust more than 10 milliseconds\r
- if (prev_delay_base - conn->our_hist.delay_base <= 10000) {\r
- conn->their_hist.Shift(prev_delay_base - conn->our_hist.delay_base);\r
- }\r
- }\r
-*/\r
-\r
- // if the delay estimate exceeds the RTT, adjust the base_delay to\r
- // compensate\r
- if (conn->our_hist.get_value() > uint32(min_rtt)) {\r
- conn->our_hist.shift(conn->our_hist.get_value() - min_rtt);\r
- }\r
-\r
- // only apply the congestion controller on acks\r
- // if we don't have a delay measurement, there's\r
- // no point in invoking the congestion control\r
- if (actual_delay != 0 && acked_bytes >= 1)\r
- conn->apply_ledbat_ccontrol(acked_bytes, actual_delay, min_rtt);\r
-\r
- // sanity check, the other end should never ack packets\r
- // past the point we've sent\r
- if (acks <= conn->cur_window_packets) {\r
- conn->max_window_user = conn->version == 0\r
- ? pf->windowsize * PACKET_SIZE : pf1->windowsize;\r
-\r
- // If max user window is set to 0, then we startup a timer\r
- // That will reset it to 1 after 15 seconds.\r
- if (conn->max_window_user == 0)\r
- // Reset max_window_user to 1 every 15 seconds.\r
- conn->zerowindow_time = g_current_ms + 15000;\r
-\r
- // Respond to connect message\r
- // Switch to CONNECTED state.\r
- if (conn->state == CS_SYN_SENT) {\r
- conn->state = CS_CONNECTED;\r
- conn->func.on_state(conn->userdata, UTP_STATE_CONNECT);\r
-\r
- // We've sent a fin, and everything was ACKed (including the FIN),\r
- // it's safe to destroy the socket. cur_window_packets == acks\r
- // means that this packet acked all the remaining packets that\r
- // were in-flight.\r
- } else if (conn->state == CS_FIN_SENT && conn->cur_window_packets == acks) {\r
- conn->state = CS_DESTROY;\r
- }\r
-\r
- // Update fast resend counter\r
- if (wrapping_compare_less(conn->fast_resend_seq_nr, (pk_ack_nr + 1) & ACK_NR_MASK))\r
- conn->fast_resend_seq_nr = pk_ack_nr + 1;\r
-\r
- LOG_UTPV("0x%08x: fast_resend_seq_nr:%u", conn, conn->fast_resend_seq_nr);\r
-\r
- for (int i = 0; i < acks; ++i) {\r
- int ack_status = conn->ack_packet(conn->seq_nr - conn->cur_window_packets);\r
- // if ack_status is 0, the packet was acked.\r
- // if acl_stauts is 1, it means that the packet had already been acked\r
- // if it's 2, the packet has not been sent yet\r
- // We need to break this loop in the latter case. This could potentially\r
- // happen if we get an ack_nr that does not exceed what we have stuffed\r
- // into the outgoing buffer, but does exceed what we have sent\r
- if (ack_status == 2) {\r
-#ifdef _DEBUG\r
- OutgoingPacket* pkt = (OutgoingPacket*)conn->outbuf.get(conn->seq_nr - conn->cur_window_packets);\r
- assert(pkt->transmissions == 0);\r
-#endif\r
- break;\r
- }\r
- conn->cur_window_packets--;\r
- }\r
-#ifdef _DEBUG\r
- if (conn->cur_window_packets == 0) assert(conn->cur_window == 0);\r
-#endif\r
-\r
- // packets in front of this may have been acked by a\r
- // selective ack (EACK). Keep decreasing the window packet size\r
- // until we hit a packet that is still waiting to be acked\r
- // in the send queue\r
- // this is especially likely to happen when the other end\r
- // has the EACK send bug older versions of uTP had\r
- while (conn->cur_window_packets > 0 && !conn->outbuf.get(conn->seq_nr - conn->cur_window_packets))\r
- conn->cur_window_packets--;\r
-\r
-#ifdef _DEBUG\r
- if (conn->cur_window_packets == 0) assert(conn->cur_window == 0);\r
-#endif\r
-\r
- // this invariant should always be true\r
- assert(conn->cur_window_packets == 0 || conn->outbuf.get(conn->seq_nr - conn->cur_window_packets));\r
-\r
- // flush Nagle\r
- if (conn->cur_window_packets == 1) {\r
- OutgoingPacket *pkt = (OutgoingPacket*)conn->outbuf.get(conn->seq_nr - 1);\r
- // do we still have quota?\r
- if (pkt->transmissions == 0 &&\r
- (!(USE_PACKET_PACING) || conn->send_quota / 100 >= (int32)pkt->length)) {\r
- conn->send_packet(pkt);\r
-\r
- // No need to send another ack if there is nothing to reorder.\r
- if (conn->reorder_count == 0) {\r
- conn->sent_ack();\r
- }\r
- }\r
- }\r
-\r
- // Fast timeout-retry\r
- if (conn->fast_timeout) {\r
- LOG_UTPV("Fast timeout %u,%u,%u?", (uint)conn->cur_window, conn->seq_nr - conn->timeout_seq_nr, conn->timeout_seq_nr);\r
- // if the fast_resend_seq_nr is not pointing to the oldest outstanding packet, it suggests that we've already\r
- // resent the packet that timed out, and we should leave the fast-timeout mode.\r
- if (((conn->seq_nr - conn->cur_window_packets) & ACK_NR_MASK) != conn->fast_resend_seq_nr) {\r
- conn->fast_timeout = false;\r
- } else {\r
- // resend the oldest packet and increment fast_resend_seq_nr\r
- // to not allow another fast resend on it again\r
- OutgoingPacket *pkt = (OutgoingPacket*)conn->outbuf.get(conn->seq_nr - conn->cur_window_packets);\r
- if (pkt && pkt->transmissions > 0) {\r
- LOG_UTPV("0x%08x: Packet %u fast timeout-retry.", conn, conn->seq_nr - conn->cur_window_packets);\r
-#ifdef _DEBUG\r
- ++conn->_stats._fastrexmit;\r
-#endif\r
- conn->fast_resend_seq_nr++;\r
- conn->send_packet(pkt);\r
- }\r
- }\r
- }\r
- }\r
-\r
- // Process selective acknowledgent\r
- if (selack_ptr != NULL) {\r
- conn->selective_ack(pk_ack_nr + 2, selack_ptr, selack_ptr[-1]);\r
- }\r
-\r
- // this invariant should always be true\r
- assert(conn->cur_window_packets == 0 || conn->outbuf.get(conn->seq_nr - conn->cur_window_packets));\r
-\r
- LOG_UTPV("0x%08x: acks:%d acked_bytes:%u seq_nr:%u cur_window:%u cur_window_packets:%u quota:%d",\r
- conn, acks, (uint)acked_bytes, conn->seq_nr, (uint)conn->cur_window, conn->cur_window_packets,\r
- conn->send_quota / 100);\r
-\r
- // In case the ack dropped the current window below\r
- // the max_window size, Mark the socket as writable\r
- if (conn->state == CS_CONNECTED_FULL && conn->is_writable(conn->get_packet_size())) {\r
- conn->state = CS_CONNECTED;\r
- LOG_UTPV("0x%08x: Socket writable. max_window:%u cur_window:%u quota:%d packet_size:%u",\r
- conn, (uint)conn->max_window, (uint)conn->cur_window, conn->send_quota / 100, (uint)conn->get_packet_size());\r
- conn->func.on_state(conn->userdata, UTP_STATE_WRITABLE);\r
- }\r
-\r
- if (pk_flags == ST_STATE) {\r
- // This is a state packet only.\r
- return 0;\r
- }\r
-\r
- // The connection is not in a state that can accept data?\r
- if (conn->state != CS_CONNECTED &&\r
- conn->state != CS_CONNECTED_FULL &&\r
- conn->state != CS_FIN_SENT) {\r
- return 0;\r
- }\r
-\r
- // Is this a finalize packet?\r
- if (pk_flags == ST_FIN && !conn->got_fin) {\r
- LOG_UTPV("Got FIN eof_pkt:%u", pk_seq_nr);\r
- conn->got_fin = true;\r
- conn->eof_pkt = pk_seq_nr;\r
- // at this point, it is possible for the\r
- // other end to have sent packets with\r
- // sequence numbers higher than seq_nr.\r
- // if this is the case, our reorder_count\r
- // is out of sync. This case is dealt with\r
- // when we re-order and hit the eof_pkt.\r
- // we'll just ignore any packets with\r
- // sequence numbers past this\r
- }\r
-\r
- // Getting an in-order packet?\r
- if (seqnr == 0) {\r
- size_t count = packet_end - data;\r
- if (count > 0 && conn->state != CS_FIN_SENT) {\r
- LOG_UTPV("0x%08x: Got Data len:%u (rb:%u)", conn, (uint)count, (uint)conn->func.get_rb_size(conn->userdata));\r
- // Post bytes to the upper layer\r
- conn->func.on_read(conn->userdata, data, count);\r
- }\r
- conn->ack_nr++;\r
- conn->bytes_since_ack += count;\r
-\r
- // Check if the next packet has been received too, but waiting\r
- // in the reorder buffer.\r
- for (;;) {\r
-\r
- if (conn->got_fin && conn->eof_pkt == conn->ack_nr) {\r
- if (conn->state != CS_FIN_SENT) {\r
- conn->state = CS_GOT_FIN;\r
- conn->rto_timeout = g_current_ms + min<uint>(conn->rto * 3, 60);\r
-\r
- LOG_UTPV("0x%08x: Posting EOF", conn);\r
- conn->func.on_state(conn->userdata, UTP_STATE_EOF);\r
- }\r
-\r
- // if the other end wants to close, ack immediately\r
- conn->send_ack();\r
-\r
- // reorder_count is not necessarily 0 at this point.\r
- // even though it is most of the time, the other end\r
- // may have sent packets with higher sequence numbers\r
- // than what later end up being eof_pkt\r
- // since we have received all packets up to eof_pkt\r
- // just ignore the ones after it.\r
- conn->reorder_count = 0;\r
- }\r
-\r
- // Quick get-out in case there is nothing to reorder\r
- if (conn->reorder_count == 0)\r
- break;\r
-\r
- // Check if there are additional buffers in the reorder buffers\r
- // that need delivery.\r
- byte *p = (byte*)conn->inbuf.get(conn->ack_nr+1);\r
- if (p == NULL)\r
- break;\r
- conn->inbuf.put(conn->ack_nr+1, NULL);\r
- count = *(uint*)p;\r
- if (count > 0 && conn->state != CS_FIN_SENT) {\r
- // Pass the bytes to the upper layer\r
- conn->func.on_read(conn->userdata, p + sizeof(uint), count);\r
- }\r
- conn->ack_nr++;\r
- conn->bytes_since_ack += count;\r
-\r
- // Free the element from the reorder buffer\r
- free(p);\r
- assert(conn->reorder_count > 0);\r
- conn->reorder_count--;\r
- }\r
-\r
- // start the delayed ACK timer\r
- conn->ack_time = g_current_ms + min<uint>(conn->ack_time - g_current_ms, DELAYED_ACK_TIME_THRESHOLD);\r
- } else {\r
- // Getting an out of order packet.\r
- // The packet needs to be remembered and rearranged later.\r
-\r
- // if we have received a FIN packet, and the EOF-sequence number\r
- // is lower than the sequence number of the packet we just received\r
- // something is wrong.\r
- if (conn->got_fin && pk_seq_nr > conn->eof_pkt) {\r
- LOG_UTPV("0x%08x: Got an invalid packet sequence number, past EOF "\r
- "reorder_count:%u len:%u (rb:%u)",\r
- conn, conn->reorder_count, (uint)(packet_end - data), (uint)conn->func.get_rb_size(conn->userdata));\r
- return 0;\r
- }\r
-\r
- // if the sequence number is entirely off the expected\r
- // one, just drop it. We can't allocate buffer space in\r
- // the inbuf entirely based on untrusted input\r
- if (seqnr > 0x3ff) {\r
- LOG_UTPV("0x%08x: Got an invalid packet sequence number, too far off "\r
- "reorder_count:%u len:%u (rb:%u)",\r
- conn, conn->reorder_count, (uint)(packet_end - data), (uint)conn->func.get_rb_size(conn->userdata));\r
- return 0;\r
- }\r
-\r
- // we need to grow the circle buffer before we\r
- // check if the packet is already in here, so that\r
- // we don't end up looking at an older packet (since\r
- // the indices wraps around).\r
- conn->inbuf.ensure_size(pk_seq_nr + 1, seqnr + 1);\r
-\r
- // Has this packet already been received? (i.e. a duplicate)\r
- // If that is the case, just discard it.\r
- if (conn->inbuf.get(pk_seq_nr) != NULL) {\r
-#ifdef _DEBUG\r
- ++conn->_stats._nduprecv;\r
-#endif\r
- return 0;\r
- }\r
-\r
- // Allocate memory to fit the packet that needs to re-ordered\r
- byte *mem = (byte*)malloc((packet_end - data) + sizeof(uint));\r
- *(uint*)mem = (uint)(packet_end - data);\r
- memcpy(mem + sizeof(uint), data, packet_end - data);\r
-\r
- // Insert into reorder buffer and increment the count\r
- // of # of packets to be reordered.\r
- // we add one to seqnr in order to leave the last\r
- // entry empty, that way the assert in send_ack\r
- // is valid. we have to add one to seqnr too, in order\r
- // to make the circular buffer grow around the correct\r
- // point (which is conn->ack_nr + 1).\r
- assert(conn->inbuf.get(pk_seq_nr) == NULL);\r
- assert((pk_seq_nr & conn->inbuf.mask) != ((conn->ack_nr+1) & conn->inbuf.mask));\r
- conn->inbuf.put(pk_seq_nr, mem);\r
- conn->reorder_count++;\r
-\r
- LOG_UTPV("0x%08x: Got out of order data reorder_count:%u len:%u (rb:%u)",\r
- conn, conn->reorder_count, (uint)(packet_end - data), (uint)conn->func.get_rb_size(conn->userdata));\r
-\r
- // Setup so the partial ACK message will get sent immediately.\r
- conn->ack_time = g_current_ms + min<uint>(conn->ack_time - g_current_ms, 1);\r
- }\r
-\r
- // If ack_time or ack_bytes indicate that we need to send and ack, send one\r
- // here instead of waiting for the timer to trigger\r
- LOG_UTPV("bytes_since_ack:%u ack_time:%d",\r
- (uint)conn->bytes_since_ack, (int)(g_current_ms - conn->ack_time));\r
- if (conn->state == CS_CONNECTED || conn->state == CS_CONNECTED_FULL) {\r
- if (conn->bytes_since_ack > DELAYED_ACK_BYTE_THRESHOLD ||\r
- (int)(g_current_ms - conn->ack_time) >= 0) {\r
- conn->send_ack();\r
- }\r
- }\r
- return (size_t)(packet_end - data);\r
-}\r
-\r
-inline bool UTP_IsV1(PacketFormatV1 const* pf)\r
-{\r
- return pf->version() == 1 && pf->type() < ST_NUM_STATES && pf->ext < 3;\r
-}\r
-\r
-void UTP_Free(UTPSocket *conn)\r
-{\r
- LOG_UTPV("0x%08x: Killing socket", conn);\r
-\r
- conn->func.on_state(conn->userdata, UTP_STATE_DESTROYING);\r
- UTP_SetCallbacks(conn, NULL, NULL);\r
-\r
- assert(conn->idx < g_utp_sockets.GetCount());\r
- assert(g_utp_sockets[conn->idx] == conn);\r
-\r
- // Unlink object from the global list\r
- assert(g_utp_sockets.GetCount() > 0);\r
-\r
- UTPSocket *last = g_utp_sockets[g_utp_sockets.GetCount() - 1];\r
-\r
- assert(last->idx < g_utp_sockets.GetCount());\r
- assert(g_utp_sockets[last->idx] == last);\r
-\r
- last->idx = conn->idx;\r
- \r
- g_utp_sockets[conn->idx] = last;\r
-\r
- // Decrease the count\r
- g_utp_sockets.SetCount(g_utp_sockets.GetCount() - 1);\r
-\r
- // Free all memory occupied by the socket object.\r
- for (size_t i = 0; i <= conn->inbuf.mask; i++) {\r
- free(conn->inbuf.elements[i]);\r
- }\r
- for (size_t i = 0; i <= conn->outbuf.mask; i++) {\r
- free(conn->outbuf.elements[i]);\r
- }\r
- free(conn->inbuf.elements);\r
- free(conn->outbuf.elements);\r
-\r
- // Finally free the socket object\r
- free(conn);\r
-}\r
-\r
-\r
-// Public functions:\r
-///////////////////////////////////////////////////////////////////////////////\r
-\r
-// Create a UTP socket\r
-UTPSocket *UTP_Create(SendToProc *send_to_proc, void *send_to_userdata, const struct sockaddr *addr, socklen_t addrlen)\r
-{\r
- UTPSocket *conn = (UTPSocket*)calloc(1, sizeof(UTPSocket));\r
-\r
- g_current_ms = UTP_GetMilliseconds();\r
-\r
- UTP_SetCallbacks(conn, NULL, NULL);\r
- conn->our_hist.clear();\r
- conn->their_hist.clear();\r
- conn->rto = 3000;\r
- conn->rtt_var = 800;\r
- conn->seq_nr = 1;\r
- conn->ack_nr = 0;\r
- conn->max_window_user = 255 * PACKET_SIZE;\r
- conn->addr = PackedSockAddr((const SOCKADDR_STORAGE*)addr, addrlen);\r
- conn->send_to_proc = send_to_proc;\r
- conn->send_to_userdata = send_to_userdata;\r
- conn->ack_time = g_current_ms + 0x70000000;\r
- conn->last_got_packet = g_current_ms;\r
- conn->last_sent_packet = g_current_ms;\r
- conn->last_measured_delay = g_current_ms + 0x70000000;\r
- conn->last_rwin_decay = int32(g_current_ms) - MAX_WINDOW_DECAY;\r
- conn->last_send_quota = g_current_ms;\r
- conn->send_quota = PACKET_SIZE * 100;\r
- conn->cur_window_packets = 0;\r
- conn->fast_resend_seq_nr = conn->seq_nr;\r
-\r
- // default to version 1\r
- UTP_SetSockopt(conn, SO_UTPVERSION, 1);\r
-\r
- // we need to fit one packet in the window\r
- // when we start the connection\r
- conn->max_window = conn->get_packet_size();\r
- conn->state = CS_IDLE;\r
-\r
- conn->outbuf.mask = 15;\r
- conn->inbuf.mask = 15;\r
-\r
- conn->outbuf.elements = (void**)calloc(16, sizeof(void*));\r
- conn->inbuf.elements = (void**)calloc(16, sizeof(void*));\r
-\r
- conn->idx = g_utp_sockets.Append(conn);\r
-\r
- LOG_UTPV("0x%08x: UTP_Create", conn);\r
-\r
- return conn;\r
-}\r
-\r
-void UTP_SetCallbacks(UTPSocket *conn, UTPFunctionTable *funcs, void *userdata)\r
-{\r
- assert(conn);\r
-\r
- if (funcs == NULL) {\r
- funcs = &zero_funcs;\r
- }\r
- conn->func = *funcs;\r
- conn->userdata = userdata;\r
-}\r
-\r
-bool UTP_SetSockopt(UTPSocket* conn, int opt, int val)\r
-{\r
- assert(conn);\r
-\r
- switch (opt) {\r
- case SO_SNDBUF:\r
- assert(val >= 1);\r
- conn->opt_sndbuf = val;\r
- return true;\r
- case SO_RCVBUF:\r
- conn->opt_rcvbuf = val;\r
- return true;\r
- case SO_UTPVERSION:\r
- assert(conn->state == CS_IDLE);\r
- if (conn->state != CS_IDLE) {\r
- // too late\r
- return false;\r
- }\r
- if (conn->version == 1 && val == 0) {\r
- conn->reply_micro = INT_MAX;\r
- conn->opt_rcvbuf = 200 * 1024;\r
- conn->opt_sndbuf = OUTGOING_BUFFER_MAX_SIZE * PACKET_SIZE;\r
- } else if (conn->version == 0 && val == 1) {\r
- conn->reply_micro = 0;\r
- conn->opt_rcvbuf = 3 * 1024 * 1024 + 512 * 1024;\r
- conn->opt_sndbuf = conn->opt_rcvbuf;\r
- }\r
- conn->version = val;\r
- return true;\r
- }\r
-\r
- return false;\r
-}\r
-\r
-// Try to connect to a specified host.\r
-// 'initial' is the number of data bytes to send in the connect packet.\r
-void UTP_Connect(UTPSocket *conn)\r
-{\r
- assert(conn);\r
-\r
- assert(conn->state == CS_IDLE);\r
- assert(conn->cur_window_packets == 0);\r
- assert(conn->outbuf.get(conn->seq_nr) == NULL);\r
- assert(sizeof(PacketFormatV1) == 20);\r
-\r
- conn->state = CS_SYN_SENT;\r
-\r
- g_current_ms = UTP_GetMilliseconds();\r
-\r
- // Create and send a connect message\r
- uint32 conn_seed = UTP_Random();\r
-\r
- // we identify newer versions by setting the\r
- // first two bytes to 0x0001\r
- if (conn->version > 0) {\r
- conn_seed &= 0xffff;\r
- }\r
-\r
- // used in parse_log.py\r
- LOG_UTP("0x%08x: UTP_Connect conn_seed:%u packet_size:%u (B) "\r
- "target_delay:%u (ms) delay_history:%u "\r
- "delay_base_history:%u (minutes)",\r
- conn, conn_seed, PACKET_SIZE, CCONTROL_TARGET / 1000,\r
- CUR_DELAY_SIZE, DELAY_BASE_HISTORY);\r
-\r
- // Setup initial timeout timer.\r
- conn->retransmit_timeout = 3000;\r
- conn->rto_timeout = g_current_ms + conn->retransmit_timeout;\r
- conn->last_rcv_win = conn->get_rcv_window();\r
-\r
- conn->conn_seed = conn_seed;\r
- conn->conn_id_recv = conn_seed;\r
- conn->conn_id_send = conn_seed+1;\r
- // if you need compatibiltiy with 1.8.1, use this. it increases attackability though.\r
- //conn->seq_nr = 1;\r
- conn->seq_nr = UTP_Random();\r
-\r
- // Create the connect packet.\r
- const size_t header_ext_size = conn->get_header_extensions_size();\r
-\r
- OutgoingPacket *pkt = (OutgoingPacket*)malloc(sizeof(OutgoingPacket) - 1 + header_ext_size);\r
-\r
- PacketFormatExtensions* p = (PacketFormatExtensions*)pkt->data;\r
- PacketFormatExtensionsV1* p1 = (PacketFormatExtensionsV1*)pkt->data;\r
-\r
- memset(p, 0, header_ext_size);\r
- // SYN packets are special, and have the receive ID in the connid field,\r
- // instead of conn_id_send.\r
- if (conn->version == 0) {\r
- p->pf.connid = conn->conn_id_recv;\r
- p->pf.ext = 2;\r
- p->pf.windowsize = (byte)DIV_ROUND_UP(conn->last_rcv_win, PACKET_SIZE);\r
- p->pf.seq_nr = conn->seq_nr;\r
- p->pf.flags = ST_SYN;\r
- p->ext_next = 0;\r
- p->ext_len = 8;\r
- memset(p->extensions, 0, 8);\r
- } else {\r
- p1->pf.set_version(1);\r
- p1->pf.set_type(ST_SYN);\r
- p1->pf.ext = 2;\r
- p1->pf.connid = conn->conn_id_recv;\r
- p1->pf.windowsize = (uint32)conn->last_rcv_win;\r
- p1->pf.seq_nr = conn->seq_nr;\r
- p1->ext_next = 0;\r
- p1->ext_len = 8;\r
- memset(p1->extensions, 0, 8);\r
- }\r
- pkt->transmissions = 0;\r
- pkt->length = header_ext_size;\r
- pkt->payload = 0;\r
-\r
- //LOG_UTPV("0x%08x: Sending connect %s [%u].",\r
- // conn, addrfmt(conn->addr, addrbuf), conn_seed);\r
-\r
- // Remember the message in the outgoing queue.\r
- conn->outbuf.ensure_size(conn->seq_nr, conn->cur_window_packets);\r
- conn->outbuf.put(conn->seq_nr, pkt);\r
- conn->seq_nr++;\r
- conn->cur_window_packets++;\r
-\r
- conn->send_packet(pkt);\r
-}\r
-\r
-bool UTP_IsIncomingUTP(UTPGotIncomingConnection *incoming_proc,\r
- SendToProc *send_to_proc, void *send_to_userdata,\r
- const byte *buffer, size_t len, const struct sockaddr *to, socklen_t tolen)\r
-{\r
- const PackedSockAddr addr((const SOCKADDR_STORAGE*)to, tolen);\r
-\r
- if (len < sizeof(PacketFormat) && len < sizeof(PacketFormatV1)) {\r
- LOG_UTPV("recv %s len:%u too small", addrfmt(addr, addrbuf), (uint)len);\r
- return false;\r
- }\r
-\r
- const PacketFormat* p = (PacketFormat*)buffer;\r
- const PacketFormatV1* p1 = (PacketFormatV1*)buffer;\r
-\r
- const byte version = UTP_IsV1(p1);\r
- const uint32 id = (version == 0) ? p->connid : uint32(p1->connid);\r
-\r
- if (version == 0 && len < sizeof(PacketFormat)) {\r
- LOG_UTPV("recv %s len:%u version:%u too small", addrfmt(addr, addrbuf), (uint)len, version);\r
- return false;\r
- }\r
-\r
- if (version == 1 && len < sizeof(PacketFormatV1)) {\r
- LOG_UTPV("recv %s len:%u version:%u too small", addrfmt(addr, addrbuf), (uint)len, version);\r
- return false;\r
- }\r
-\r
- LOG_UTPV("recv %s len:%u id:%u", addrfmt(addr, addrbuf), (uint)len, id);\r
-\r
- const PacketFormat *pf = (PacketFormat*)p;\r
- const PacketFormatV1 *pf1 = (PacketFormatV1*)p;\r
-\r
- if (version == 0) {\r
- LOG_UTPV("recv id:%u seq_nr:%u ack_nr:%u", id, (uint)pf->seq_nr, (uint)pf->ack_nr);\r
- } else {\r
- LOG_UTPV("recv id:%u seq_nr:%u ack_nr:%u", id, (uint)pf1->seq_nr, (uint)pf1->ack_nr);\r
- }\r
-\r
- const byte flags = version == 0 ? pf->flags : pf1->type();\r
-\r
- for (size_t i = 0; i < g_utp_sockets.GetCount(); i++) {\r
- UTPSocket *conn = g_utp_sockets[i];\r
- //LOG_UTPV("Examining UTPSocket %s for %s and (seed:%u s:%u r:%u) for %u",\r
- // addrfmt(conn->addr, addrbuf), addrfmt(addr, addrbuf2), conn->conn_seed, conn->conn_id_send, conn->conn_id_recv, id);\r
- if (conn->addr != addr)\r
- continue;\r
-\r
- if (flags == ST_RESET && (conn->conn_id_send == id || conn->conn_id_recv == id)) {\r
- LOG_UTPV("0x%08x: recv RST for existing connection", conn);\r
- if (!conn->userdata || conn->state == CS_FIN_SENT) {\r
- conn->state = CS_DESTROY;\r
- } else {\r
- conn->state = CS_RESET;\r
- }\r
- if (conn->userdata) {\r
- conn->func.on_overhead(conn->userdata, false, len + conn->get_udp_overhead(),\r
- close_overhead);\r
- const int err = conn->state == CS_SYN_SENT ?\r
- ECONNREFUSED :\r
- ECONNRESET;\r
- conn->func.on_error(conn->userdata, err);\r
- }\r
- return true;\r
- } else if (flags != ST_SYN && conn->conn_id_recv == id) {\r
- LOG_UTPV("0x%08x: recv processing", conn);\r
- const size_t read = UTP_ProcessIncoming(conn, buffer, len);\r
- if (conn->userdata) {\r
- conn->func.on_overhead(conn->userdata, false,\r
- (len - read) + conn->get_udp_overhead(),\r
- header_overhead);\r
- }\r
- return true;\r
- }\r
- }\r
-\r
- if (flags == ST_RESET) {\r
- LOG_UTPV("recv RST for unknown connection");\r
- return true;\r
- }\r
-\r
- const uint32 seq_nr = version == 0 ? pf->seq_nr : pf1->seq_nr;\r
- if (flags != ST_SYN) {\r
- for (size_t i = 0; i < g_rst_info.GetCount(); i++) {\r
- if (g_rst_info[i].connid != id)\r
- continue;\r
- if (g_rst_info[i].addr != addr)\r
- continue;\r
- if (seq_nr != g_rst_info[i].ack_nr)\r
- continue;\r
- g_rst_info[i].timestamp = UTP_GetMilliseconds();\r
- LOG_UTPV("recv not sending RST to non-SYN (stored)");\r
- return true;\r
- }\r
- if (g_rst_info.GetCount() > RST_INFO_LIMIT) {\r
- LOG_UTPV("recv not sending RST to non-SYN (limit at %u stored)", (uint)g_rst_info.GetCount());\r
- return true;\r
- }\r
- LOG_UTPV("recv send RST to non-SYN (%u stored)", (uint)g_rst_info.GetCount());\r
- RST_Info &r = g_rst_info.Append();\r
- r.addr = addr;\r
- r.connid = id;\r
- r.ack_nr = seq_nr;\r
- r.timestamp = UTP_GetMilliseconds();\r
-\r
- UTPSocket::send_rst(send_to_proc, send_to_userdata, addr, id, seq_nr, UTP_Random(), version);\r
- return true;\r
- }\r
-\r
- if (incoming_proc) {\r
- LOG_UTPV("Incoming connection from %s uTP version:%u", addrfmt(addr, addrbuf), version);\r
-\r
- // Create a new UTP socket to handle this new connection\r
- UTPSocket *conn = UTP_Create(send_to_proc, send_to_userdata, to, tolen);\r
- // Need to track this value to be able to detect duplicate CONNECTs\r
- conn->conn_seed = id;\r
- // This is value that identifies this connection for them.\r
- conn->conn_id_send = id;\r
- // This is value that identifies this connection for us.\r
- conn->conn_id_recv = id+1;\r
- conn->ack_nr = seq_nr;\r
- conn->seq_nr = UTP_Random();\r
- conn->fast_resend_seq_nr = conn->seq_nr;\r
-\r
- UTP_SetSockopt(conn, SO_UTPVERSION, version);\r
- conn->state = CS_CONNECTED;\r
-\r
- const size_t read = UTP_ProcessIncoming(conn, buffer, len, true);\r
-\r
- LOG_UTPV("0x%08x: recv send connect ACK", conn);\r
- conn->send_ack(true);\r
-\r
- incoming_proc(send_to_userdata, conn);\r
-\r
- // we report overhead after incoming_proc, because the callbacks are setup now\r
- if (conn->userdata) {\r
- // SYN\r
- conn->func.on_overhead(conn->userdata, false, (len - read) + conn->get_udp_overhead(),\r
- header_overhead);\r
- // SYNACK\r
- conn->func.on_overhead(conn->userdata, true, conn->get_overhead(),\r
- ack_overhead);\r
- }\r
- }\r
-\r
- return true;\r
-}\r
-\r
-bool UTP_HandleICMP(const byte* buffer, size_t len, const struct sockaddr *to, socklen_t tolen)\r
-{\r
- const PackedSockAddr addr((const SOCKADDR_STORAGE*)to, tolen);\r
-\r
- // Want the whole packet so we have connection ID\r
- if (len < sizeof(PacketFormat)) {\r
- return false;\r
- }\r
-\r
- const PacketFormat* p = (PacketFormat*)buffer;\r
- const PacketFormatV1* p1 = (PacketFormatV1*)buffer;\r
-\r
- const byte version = UTP_IsV1(p1);\r
- const uint32 id = (version == 0) ? p->connid : uint32(p1->connid);\r
-\r
- for (size_t i = 0; i < g_utp_sockets.GetCount(); ++i) {\r
- UTPSocket *conn = g_utp_sockets[i];\r
- if (conn->addr == addr &&\r
- conn->conn_id_recv == id) {\r
- // Don't pass on errors for idle/closed connections\r
- if (conn->state != CS_IDLE) {\r
- if (!conn->userdata || conn->state == CS_FIN_SENT) {\r
- LOG_UTPV("0x%08x: icmp packet causing socket destruction", conn);\r
- conn->state = CS_DESTROY;\r
- } else {\r
- conn->state = CS_RESET;\r
- }\r
- if (conn->userdata) {\r
- const int err = conn->state == CS_SYN_SENT ?\r
- ECONNREFUSED :\r
- ECONNRESET;\r
- LOG_UTPV("0x%08x: icmp packet causing error on socket:%d", conn, err);\r
- conn->func.on_error(conn->userdata, err);\r
- }\r
- }\r
- return true;\r
- }\r
- }\r
- return false;\r
-}\r
-\r
-// Write bytes to the UTP socket.\r
-// Returns true if the socket is still writable.\r
-bool UTP_Write(UTPSocket *conn, size_t bytes)\r
-{\r
- assert(conn);\r
-\r
-#ifdef g_log_utp_verbose\r
- size_t param = bytes;\r
-#endif\r
-\r
- if (conn->state != CS_CONNECTED) {\r
- LOG_UTPV("0x%08x: UTP_Write %u bytes = false (not CS_CONNECTED)", conn, (uint)bytes);\r
- return false;\r
- }\r
-\r
- g_current_ms = UTP_GetMilliseconds();\r
-\r
- conn->update_send_quota();\r
-\r
- // don't send unless it will all fit in the window\r
- size_t packet_size = conn->get_packet_size();\r
- size_t num_to_send = min<size_t>(bytes, packet_size);\r
- while (conn->is_writable(num_to_send)) {\r
- // Send an outgoing packet.\r
- // Also add it to the outgoing of packets that have been sent but not ACKed.\r
-\r
- if (num_to_send == 0) {\r
- LOG_UTPV("0x%08x: UTP_Write %u bytes = true", conn, (uint)param);\r
- return true;\r
- }\r
- bytes -= num_to_send;\r
-\r
- LOG_UTPV("0x%08x: Sending packet. seq_nr:%u ack_nr:%u wnd:%u/%u/%u rcv_win:%u size:%u quota:%d cur_window_packets:%u",\r
- conn, conn->seq_nr, conn->ack_nr,\r
- (uint)(conn->cur_window + num_to_send),\r
- (uint)conn->max_window, (uint)conn->max_window_user,\r
- (uint)conn->last_rcv_win, num_to_send, conn->send_quota / 100,\r
- conn->cur_window_packets);\r
- conn->write_outgoing_packet(num_to_send, ST_DATA);\r
- num_to_send = min<size_t>(bytes, packet_size);\r
- }\r
-\r
- // mark the socket as not being writable.\r
- conn->state = CS_CONNECTED_FULL;\r
- LOG_UTPV("0x%08x: UTP_Write %u bytes = false", conn, (uint)bytes);\r
- return false;\r
-}\r
-\r
-void UTP_RBDrained(UTPSocket *conn)\r
-{\r
- assert(conn);\r
-\r
- const size_t rcvwin = conn->get_rcv_window();\r
-\r
- if (rcvwin > conn->last_rcv_win) {\r
- // If last window was 0 send ACK immediately, otherwise should set timer\r
- if (conn->last_rcv_win == 0) {\r
- conn->send_ack();\r
- } else {\r
- conn->ack_time = g_current_ms + min<uint>(conn->ack_time - g_current_ms, DELAYED_ACK_TIME_THRESHOLD);\r
- }\r
- }\r
-}\r
-\r
-void UTP_CheckTimeouts()\r
-{\r
- g_current_ms = UTP_GetMilliseconds();\r
-\r
- for (size_t i = 0; i < g_rst_info.GetCount(); i++) {\r
- if ((int)(g_current_ms - g_rst_info[i].timestamp) >= RST_INFO_TIMEOUT) {\r
- g_rst_info.MoveUpLast(i);\r
- i--;\r
- }\r
- }\r
- if (g_rst_info.GetCount() != g_rst_info.GetAlloc()) {\r
- g_rst_info.Compact();\r
- }\r
-\r
- for (size_t i = 0; i != g_utp_sockets.GetCount(); i++) {\r
- UTPSocket *conn = g_utp_sockets[i];\r
- conn->check_timeouts();\r
-\r
- // Check if the object was deleted\r
- if (conn->state == CS_DESTROY) {\r
- LOG_UTPV("0x%08x: Destroying", conn);\r
- UTP_Free(conn);\r
- i--;\r
- }\r
- }\r
-}\r
-\r
-size_t UTP_GetPacketSize(UTPSocket *socket)\r
-{\r
- return socket->get_packet_size();\r
-}\r
-\r
-void UTP_GetPeerName(UTPSocket *conn, struct sockaddr *addr, socklen_t *addrlen)\r
-{\r
- assert(conn);\r
-\r
- socklen_t len;\r
- const SOCKADDR_STORAGE sa = conn->addr.get_sockaddr_storage(&len);\r
- *addrlen = min(len, *addrlen);\r
- memcpy(addr, &sa, *addrlen);\r
-}\r
-\r
-void UTP_GetDelays(UTPSocket *conn, int32 *ours, int32 *theirs, uint32 *age)\r
-{\r
- assert(conn);\r
-\r
- if (ours) *ours = conn->our_hist.get_value();\r
- if (theirs) *theirs = conn->their_hist.get_value();\r
- if (age) *age = g_current_ms - conn->last_measured_delay;\r
-}\r
-\r
-#ifdef _DEBUG\r
-void UTP_GetStats(UTPSocket *conn, UTPStats *stats)\r
-{\r
- assert(conn);\r
-\r
- *stats = conn->_stats;\r
-}\r
-#endif // _DEBUG\r
-\r
-void UTP_GetGlobalStats(UTPGlobalStats *stats)\r
-{\r
- *stats = _global_stats;\r
-}\r
-\r
-// Close the UTP socket.\r
-// It is not valid for the upper layer to refer to socket after it is closed.\r
-// Data will keep to try being delivered after the close.\r
-void UTP_Close(UTPSocket *conn)\r
-{\r
- assert(conn);\r
-\r
- assert(conn->state != CS_DESTROY_DELAY && conn->state != CS_FIN_SENT && conn->state != CS_DESTROY);\r
-\r
- LOG_UTPV("0x%08x: UTP_Close in state:%s", conn, statenames[conn->state]);\r
-\r
- switch(conn->state) {\r
- case CS_CONNECTED:\r
- case CS_CONNECTED_FULL:\r
- conn->state = CS_FIN_SENT;\r
- conn->write_outgoing_packet(0, ST_FIN);\r
- break;\r
-\r
- case CS_SYN_SENT:\r
- conn->rto_timeout = UTP_GetMilliseconds() + min<uint>(conn->rto * 2, 60);\r
- case CS_GOT_FIN:\r
- conn->state = CS_DESTROY_DELAY;\r
- break;\r
-\r
- default:\r
- conn->state = CS_DESTROY;\r
- break;\r
- }\r
-}\r
+++ /dev/null
-#ifndef __UTP_H__\r
-#define __UTP_H__\r
-\r
-#include "utypes.h"\r
-\r
-#ifdef WIN32\r
-#define _CRT_SECURE_NO_DEPRECATE\r
-#define WIN32_LEAN_AND_MEAN\r
-#include <windows.h>\r
-#include <winsock2.h>\r
-#include <ws2tcpip.h>\r
-#pragma comment(lib,"ws2_32.lib")\r
-#else\r
-#include <stdlib.h>\r
-#include <sys/types.h>\r
-#include <sys/socket.h>\r
-#include <netinet/in.h>\r
-#include <arpa/inet.h>\r
-#endif\r
-\r
-#ifdef __cplusplus\r
-extern "C" {\r
-#endif\r
-\r
-struct UTPSocket;\r
-\r
-// Used to set sockopt on a uTP socket to set the version of uTP\r
-// to use for outgoing connections. This can only be called before\r
-// the uTP socket is connected\r
-#define SO_UTPVERSION 99\r
-\r
-enum {\r
- // socket has reveived syn-ack (notification only for outgoing connection completion)\r
- // this implies writability\r
- UTP_STATE_CONNECT = 1,\r
-\r
- // socket is able to send more data\r
- UTP_STATE_WRITABLE = 2,\r
-\r
- // connection closed\r
- UTP_STATE_EOF = 3,\r
-\r
- // socket is being destroyed, meaning all data has been sent if possible.\r
- // it is not valid to refer to the socket after this state change occurs\r
- UTP_STATE_DESTROYING = 4,\r
-};\r
-\r
-// Callbacks called by a uTP socket (register with UTP_SetCallbacks)\r
-\r
-// The uTP socket layer calls this when bytes have been received from the network.\r
-typedef void UTPOnReadProc(void *userdata, const byte *bytes, size_t count);\r
-\r
-// The uTP socket layer calls this to fill the outgoing buffer with bytes.\r
-// The uTP layer takes responsibility that those bytes will be delivered.\r
-typedef void UTPOnWriteProc(void *userdata, byte *bytes, size_t count);\r
-\r
-// The uTP socket layer calls this to retrieve number of bytes currently in read buffer\r
-typedef size_t UTPGetRBSize(void *userdata);\r
-\r
-// The uTP socket layer calls this whenever the socket becomes writable.\r
-typedef void UTPOnStateChangeProc(void *userdata, int state);\r
-\r
-// The uTP socket layer calls this when an error occurs on the socket.\r
-// These errors currently include ECONNREFUSED, ECONNRESET and ETIMEDOUT, but\r
-// could eventually include any BSD socket error.\r
-typedef void UTPOnErrorProc(void *userdata, int errcode);\r
-\r
-// The uTP socket layer calls this to report overhead statistics\r
-typedef void UTPOnOverheadProc(void *userdata, bool send, size_t count, int type);\r
-\r
-struct UTPFunctionTable {\r
- UTPOnReadProc *on_read;\r
- UTPOnWriteProc *on_write;\r
- UTPGetRBSize *get_rb_size;\r
- UTPOnStateChangeProc *on_state;\r
- UTPOnErrorProc *on_error;\r
- UTPOnOverheadProc *on_overhead;\r
-};\r
-\r
-\r
-// The uTP socket layer calls this when a new incoming uTP connection is established\r
-// this implies writability\r
-typedef void UTPGotIncomingConnection(void *userdata, struct UTPSocket* s);\r
-\r
-// The uTP socket layer calls this to send UDP packets\r
-typedef void SendToProc(void *userdata, const byte *p, size_t len, const struct sockaddr *to, socklen_t tolen);\r
-\r
-\r
-// Functions which can be called with a uTP socket\r
-\r
-// Create a uTP socket\r
-struct UTPSocket *UTP_Create(SendToProc *send_to_proc, void *send_to_userdata,\r
- const struct sockaddr *addr, socklen_t addrlen);\r
-\r
-// Setup the callbacks - must be done before connect or on incoming connection\r
-void UTP_SetCallbacks(struct UTPSocket *socket, struct UTPFunctionTable *func, void *userdata);\r
-\r
-// Valid options include SO_SNDBUF, SO_RCVBUF and SO_UTPVERSION\r
-bool UTP_SetSockopt(struct UTPSocket *socket, int opt, int val);\r
-\r
-// Try to connect to a specified host.\r
-void UTP_Connect(struct UTPSocket *socket);\r
-\r
-// Process a UDP packet from the network. This will process a packet for an existing connection,\r
-// or create a new connection and call incoming_proc. Returns true if the packet was processed\r
-// in some way, false if the packet did not appear to be uTP.\r
-bool UTP_IsIncomingUTP(UTPGotIncomingConnection *incoming_proc,\r
- SendToProc *send_to_proc, void *send_to_userdata,\r
- const byte *buffer, size_t len, const struct sockaddr *to, socklen_t tolen);\r
-\r
-// Process an ICMP received UDP packet.\r
-bool UTP_HandleICMP(const byte* buffer, size_t len, const struct sockaddr *to, socklen_t tolen);\r
-\r
-// Write bytes to the uTP socket.\r
-// Returns true if the socket is still writable.\r
-bool UTP_Write(struct UTPSocket *socket, size_t count);\r
-\r
-// Notify the uTP socket of buffer drain\r
-void UTP_RBDrained(struct UTPSocket *socket);\r
-\r
-// Call periodically to process timeouts and other periodic events\r
-void UTP_CheckTimeouts(void);\r
-\r
-// Retrieves the peer address of the specified socket, stores this address in the\r
-// sockaddr structure pointed to by the addr argument, and stores the length of this\r
-// address in the object pointed to by the addrlen argument.\r
-void UTP_GetPeerName(struct UTPSocket *socket, struct sockaddr *addr, socklen_t *addrlen);\r
-\r
-void UTP_GetDelays(struct UTPSocket *socket, int32 *ours, int32 *theirs, uint32 *age);\r
-\r
-size_t UTP_GetPacketSize(struct UTPSocket *socket);\r
-\r
-#ifdef _DEBUG\r
-struct UTPStats {\r
- uint64 _nbytes_recv; // total bytes received\r
- uint64 _nbytes_xmit; // total bytes transmitted\r
- uint32 _rexmit; // retransmit counter\r
- uint32 _fastrexmit; // fast retransmit counter\r
- uint32 _nxmit; // transmit counter\r
- uint32 _nrecv; // receive counter (total)\r
- uint32 _nduprecv; // duplicate receive counter\r
-};\r
-\r
-// Get stats for UTP socket\r
-void UTP_GetStats(struct UTPSocket *socket, UTPStats *stats);\r
-#endif\r
-\r
-// Close the UTP socket.\r
-// It is not valid to issue commands for this socket after it is closed.\r
-// This does not actually destroy the socket until outstanding data is sent, at which\r
-// point the socket will change to the UTP_STATE_DESTROYING state.\r
-void UTP_Close(struct UTPSocket *socket);\r
-\r
-struct UTPGlobalStats {\r
- uint32 _nraw_recv[5]; // total packets recieved less than 300/600/1200/MTU bytes fpr all connections (global)\r
- uint32 _nraw_send[5]; // total packets sent less than 300/600/1200/MTU bytes for all connections (global)\r
-};\r
-\r
-void UTP_GetGlobalStats(struct UTPGlobalStats *stats);\r
-\r
-#ifdef __cplusplus\r
-}\r
-#endif\r
-\r
-#endif //__UTP_H__\r
+++ /dev/null
-#define CCONTROL_TARGET (100 * 1000) // us
-#define RATE_CHECK_INTERVAL 10000 // ms
-#define DYNAMIC_PACKET_SIZE_ENABLED false
-#define DYNAMIC_PACKET_SIZE_FACTOR 2
-// This should return the global number of bytes sent, used for determining dynamic
-// packet size based on rate
-
-#warning implement this in libtransmission
-uint64 UTP_GetGlobalUTPBytesSent(const struct sockaddr *remote, socklen_t remotelen) { return 0; }
-
-enum bandwidth_type_t {
- payload_bandwidth, connect_overhead,
- close_overhead, ack_overhead,
- header_overhead, retransmit_overhead
-};
-
-#ifdef WIN32
-#define I64u "%I64u"
-#else
-#define I64u "%Lu"
-#endif
-#ifdef WIN32
-#define snprintf _snprintf
-#endif
-
-#define g_log_utp 0
-#define g_log_utp_verbose 0
-void utp_log(char const* fmt, ...)
-{
- /*
- printf("[%u] ", UTP_GetMilliseconds());
- va_list vl;
- va_start(vl, fmt);
- vprintf(fmt, vl);
- va_end(vl);
- puts("");
- fflush(stdout);
- */
-};
+++ /dev/null
-#define CCONTROL_TARGET (100 * 1000) // us\r
-#define RATE_CHECK_INTERVAL 10000 // ms\r
-#define DYNAMIC_PACKET_SIZE_ENABLED false\r
-#define DYNAMIC_PACKET_SIZE_FACTOR 2\r
-// This should return the global number of bytes sent, used for determining dynamic\r
-// packet size based on rate\r
-uint64 UTP_GetGlobalUTPBytesSent(const struct sockaddr *remote, socklen_t remotelen) { return 0; }\r
-\r
-enum bandwidth_type_t {\r
- payload_bandwidth, connect_overhead,\r
- close_overhead, ack_overhead,\r
- header_overhead, retransmit_overhead\r
-};\r
-\r
-#ifdef WIN32\r
-#define I64u "%I64u"\r
-#else\r
-#define I64u "%Lu"\r
-#endif\r
-#ifdef WIN32\r
-#define snprintf _snprintf\r
-#endif\r
-\r
-#define g_log_utp 0\r
-#define g_log_utp_verbose 0\r
-void utp_log(char const* fmt, ...);\r
+++ /dev/null
-#include "StdAfx.h"\r
-\r
-#include "utypes.h"\r
-#include <assert.h>\r
-#include <stdlib.h>\r
-\r
-#ifdef WIN32\r
-\r
-#define WIN32_LEAN_AND_MEAN\r
-#include <windows.h>\r
-#include <winsock2.h>\r
-#include <ws2tcpip.h>\r
-\r
-typedef ULONGLONG (WINAPI GetTickCount64Proc)(void);\r
-static GetTickCount64Proc *pt2GetTickCount64;\r
-static GetTickCount64Proc *pt2RealGetTickCount;\r
-\r
-static uint64 startPerformanceCounter;\r
-static uint64 startGetTickCount;\r
-// MSVC 6 standard doesn't like division with uint64s\r
-static double counterPerMicrosecond;\r
-\r
-uint64 UTGetTickCount64()\r
-{\r
- if (pt2GetTickCount64) {\r
- return pt2GetTickCount64();\r
- }\r
- if (pt2RealGetTickCount) {\r
- uint64 v = pt2RealGetTickCount();\r
- // fix return value from GetTickCount\r
- return (DWORD)v | ((v >> 0x18) & 0xFFFFFFFF00000000);\r
- }\r
- return (uint64)GetTickCount();\r
-}\r
-\r
-void Time_Initialize()\r
-{\r
- HMODULE kernel32 = GetModuleHandleA("kernel32.dll");\r
- pt2GetTickCount64 = (GetTickCount64Proc*)GetProcAddress(kernel32, "GetTickCount64");\r
- // not a typo. GetTickCount actually returns 64 bits\r
- pt2RealGetTickCount = (GetTickCount64Proc*)GetProcAddress(kernel32, "GetTickCount");\r
-\r
- uint64 frequency;\r
- QueryPerformanceCounter((LARGE_INTEGER*)&startPerformanceCounter);\r
- QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);\r
- counterPerMicrosecond = (double)frequency / 1000000.0f;\r
- startGetTickCount = UTGetTickCount64();\r
-}\r
-\r
-int64 abs64(int64 x) { return x < 0 ? -x : x; }\r
-\r
-static uint64 GetMicroseconds()\r
-{\r
- static bool time_init = false;\r
- if (!time_init) {\r
- time_init = true;\r
- Time_Initialize();\r
- }\r
-\r
- uint64 counter;\r
- uint64 tick;\r
-\r
- QueryPerformanceCounter((LARGE_INTEGER*) &counter);\r
- tick = UTGetTickCount64();\r
-\r
- // unfortunately, QueryPerformanceCounter is not guaranteed\r
- // to be monotonic. Make it so.\r
- int64 ret = (int64)(((int64)counter - (int64)startPerformanceCounter) / counterPerMicrosecond);\r
- // if the QPC clock leaps more than one second off GetTickCount64()\r
- // something is seriously fishy. Adjust QPC to stay monotonic\r
- int64 tick_diff = tick - startGetTickCount;\r
- if (abs64(ret / 100000 - tick_diff / 100) > 10) {\r
- startPerformanceCounter -= (uint64)((int64)(tick_diff * 1000 - ret) * counterPerMicrosecond);\r
- ret = (int64)((counter - startPerformanceCounter) / counterPerMicrosecond);\r
- }\r
- return ret;\r
-}\r
-\r
-#else //!WIN32\r
-\r
-#include <time.h>\r
-#include <sys/time.h> // Linux needs both time.h and sys/time.h\r
-#include <stdlib.h>\r
-\r
-#include <unistd.h>\r
-#include <sys/socket.h>\r
-#include <arpa/inet.h>\r
-\r
-#if defined(__APPLE__)\r
-#include <mach/mach_time.h>\r
-\r
-static uint64 GetMicroseconds()\r
-{\r
- // http://developer.apple.com/mac/library/qa/qa2004/qa1398.html\r
- // http://www.macresearch.org/tutorial_performance_and_time\r
- static mach_timebase_info_data_t sTimebaseInfo;\r
- static uint64_t start_tick = 0;\r
- uint64_t tick;\r
- // Returns a counter in some fraction of a nanoseconds\r
- tick = mach_absolute_time(); \r
- if (sTimebaseInfo.denom == 0) {\r
- // Get the timer ratio to convert mach_absolute_time to nanoseconds\r
- mach_timebase_info(&sTimebaseInfo); \r
- start_tick = tick;\r
- }\r
- // Calculate the elapsed time, convert it to microseconds and return it.\r
- return ((tick - start_tick) * sTimebaseInfo.numer) / (sTimebaseInfo.denom * 1000);\r
-}\r
-\r
-#else //!__APPLE__\r
-\r
-/* Unfortunately, #ifdef CLOCK_MONOTONIC is not enough to make sure that\r
- POSIX clocks work -- we could be running a recent libc with an ancient\r
- kernel (think OpenWRT). -- jch */\r
-\r
-static uint64_t GetMicroseconds()\r
-{\r
- static int have_posix_clocks = -1;\r
- int rc;\r
-\r
-#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(CLOCK_MONOTONIC)\r
- if (have_posix_clocks < 0) {\r
- struct timespec ts;\r
- rc = clock_gettime(CLOCK_MONOTONIC, &ts);\r
- if (rc < 0) {\r
- have_posix_clocks = 0;\r
- } else {\r
- have_posix_clocks = 1;\r
- }\r
- }\r
-\r
- if (have_posix_clocks) {\r
- struct timespec ts;\r
- rc = clock_gettime(CLOCK_MONOTONIC, &ts);\r
- return uint64(ts.tv_sec) * 1000000 + ts.tv_nsec / 1000;\r
- }\r
-#endif\r
- {\r
- struct timeval tv;\r
- rc = gettimeofday(&tv, NULL);\r
- return uint64(tv.tv_sec) * 1000000 + tv.tv_usec;\r
- }\r
-}\r
-#endif //!__APPLE__\r
-\r
-#endif //!WIN32\r
-\r
-uint64 UTP_GetMicroseconds()\r
-{\r
- static uint64 offset = 0, previous = 0;\r
-\r
- uint64 now = GetMicroseconds() + offset;\r
- if (previous > now) {\r
- /* Eek! */\r
- offset += previous - now;\r
- now = previous;\r
- }\r
- previous = now;\r
- return now;\r
-}\r
-\r
-uint32 UTP_GetMilliseconds()\r
-{\r
- return UTP_GetMicroseconds() / 1000;\r
-}\r
-\r
-\r
-#define ETHERNET_MTU 1500\r
-#define IPV4_HEADER_SIZE 20\r
-#define IPV6_HEADER_SIZE 40\r
-#define UDP_HEADER_SIZE 8\r
-#define GRE_HEADER_SIZE 24\r
-#define PPPOE_HEADER_SIZE 8\r
-#define MPPE_HEADER_SIZE 2\r
-// packets have been observed in the wild that were fragmented\r
-// with a payload of 1416 for the first fragment\r
-// There are reports of routers that have MTU sizes as small as 1392\r
-#define FUDGE_HEADER_SIZE 36\r
-#define TEREDO_MTU 1280\r
-\r
-#define UDP_IPV4_OVERHEAD (IPV4_HEADER_SIZE + UDP_HEADER_SIZE)\r
-#define UDP_IPV6_OVERHEAD (IPV6_HEADER_SIZE + UDP_HEADER_SIZE)\r
-#define UDP_TEREDO_OVERHEAD (UDP_IPV4_OVERHEAD + UDP_IPV6_OVERHEAD)\r
-\r
-#define UDP_IPV4_MTU (ETHERNET_MTU - IPV4_HEADER_SIZE - UDP_HEADER_SIZE - GRE_HEADER_SIZE - PPPOE_HEADER_SIZE - MPPE_HEADER_SIZE - FUDGE_HEADER_SIZE)\r
-#define UDP_IPV6_MTU (ETHERNET_MTU - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - GRE_HEADER_SIZE - PPPOE_HEADER_SIZE - MPPE_HEADER_SIZE - FUDGE_HEADER_SIZE)\r
-#define UDP_TEREDO_MTU (TEREDO_MTU - IPV6_HEADER_SIZE - UDP_HEADER_SIZE)\r
-\r
-uint16 UTP_GetUDPMTU(const struct sockaddr *remote, socklen_t remotelen)\r
-{\r
- // Since we don't know the local address of the interface,\r
- // be conservative and assume all IPv6 connections are Teredo.\r
- return remote->sa_family == AF_INET6 ? UDP_TEREDO_MTU : UDP_IPV4_MTU;\r
-}\r
-\r
-uint16 UTP_GetUDPOverhead(const struct sockaddr *remote, socklen_t remotelen)\r
-{\r
- // Since we don't know the local address of the interface,\r
- // be conservative and assume all IPv6 connections are Teredo.\r
- return remote->sa_family == AF_INET6 ? UDP_TEREDO_OVERHEAD : UDP_IPV4_OVERHEAD;\r
-}\r
-\r
-uint32 UTP_Random()\r
-{\r
- return rand();\r
-}\r
-\r
-void UTP_DelaySample(const struct sockaddr *remote, int sample_ms) {}\r
-size_t UTP_GetPacketSize(const struct sockaddr *remote) { return 1500; }\r
-\r
+++ /dev/null
-// This should return the MTU to the destination\r
-uint16 UTP_GetUDPMTU(const struct sockaddr *remote, socklen_t remotelen);\r
-// This should return the number of bytes of UDP overhead for one packet to the\r
-// destination, for overhead calculation only\r
-uint16 UTP_GetUDPOverhead(const struct sockaddr *remote, socklen_t remotelen);\r
-// This should return monotonically increasing milliseconds, start point does not matter\r
-uint32 UTP_GetMilliseconds();\r
-// This should return monotonically increasing microseconds, start point does not matter\r
-uint64 UTP_GetMicroseconds();\r
-// This should return a random uint32\r
-uint32 UTP_Random();\r
-// This is called every time we have a delay sample is made\r
-void UTP_DelaySample(const struct sockaddr *remote, int sample_ms);\r
-// Should return the max packet size to use when sending to the given address\r
-size_t UTP_GetPacketSize(const struct sockaddr *remote);\r
-\r
+++ /dev/null
-#ifndef __UTYPES_H__\r
-#define __UTYPES_H__\r
-\r
-// standard types\r
-typedef unsigned char byte;\r
-typedef unsigned char uint8;\r
-typedef signed char int8;\r
-typedef unsigned short uint16;\r
-typedef signed short int16;\r
-typedef unsigned int uint;\r
-typedef unsigned int uint32;\r
-typedef signed int int32;\r
-\r
-#ifdef _MSC_VER\r
-typedef unsigned __int64 uint64;\r
-typedef signed __int64 int64;\r
-#else\r
-typedef unsigned long long uint64;\r
-typedef long long int64;\r
-#endif\r
-\r
-/* compile-time assert */\r
-#ifndef CASSERT\r
-#define CASSERT( exp, name ) typedef int is_not_##name [ (exp ) ? 1 : -1 ];\r
-#endif\r
-\r
-CASSERT(8 == sizeof(uint64), sizeof_uint64_is_8)\r
-CASSERT(8 == sizeof(int64), sizeof_int64_is_8)\r
-\r
-#ifndef INT64_MAX\r
-#define INT64_MAX 0x7fffffffffffffffLL\r
-#endif\r
-\r
-// always ANSI\r
-typedef const char * cstr;\r
-typedef char * str;\r
-\r
-#ifndef __cplusplus\r
-typedef uint8 bool;\r
-#endif\r
-\r
-#endif //__UTYPES_H__\r
+++ /dev/null
-$Id: Changelog.txt,v 1.193 2014/02/05 17:26:45 nanard Exp $
-miniUPnP client Changelog.
-
-2014/02/05:
- handle EINPROGRESS after connect()
-
-2014/02/03:
- minixml now handle XML comments
-
-VERSION 1.9 : released 2014/01/31
-
-2014/01/31:
- added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
- increment API_VERSION to 10
-
-2013/12/09:
- --help and -h arguments in upnpc.c
-
-2013/10/07:
- fixed potential buffer overrun in miniwget.c
- Modified UPNP_GetValidIGD() to check for ExternalIpAddress
-
-2013/08/01:
- define MAXHOSTNAMELEN if not already done
-
-2013/06/06:
- update upnpreplyparse to allow larger values (128 chars instead of 64)
-
-2013/05/14:
- Update upnpreplyparse to take into account "empty" elements
- validate upnpreplyparse.c code with "make check"
-
-2013/05/03:
- Fix Solaris build thanks to Maciej Małecki
-
-2013/04/27:
- Fix testminiwget.sh for BSD
-
-2013/03/23:
- Fixed Makefile for *BSD
-
-2013/03/11:
- Update Makefile to use JNAerator version 0.11
-
-2013/02/11:
- Fix testminiwget.sh for use with dash
- Use $(DESTDIR) in Makefile
-
-VERSION 1.8 : released 2013/02/06
-
-2012/10/16:
- fix testminiwget with no IPv6 support
-
-2012/09/27:
- Rename all include guards to not clash with C99
- (7.1.3 Reserved identifiers).
-
-2012/08/30:
- Added -e option to upnpc program (set description for port mappings)
-
-2012/08/29:
- Python 3 support (thanks to Christopher Foo)
-
-2012/08/11:
- Fix a memory link in UPNP_GetValidIGD()
- Try to handle scope id in link local IPv6 URL under MS Windows
-
-2012/07/20:
- Disable HAS_IP_MREQN on DragonFly BSD
-
-2012/06/28:
- GetUPNPUrls() now inserts scope into link-local IPv6 addresses
-
-2012/06/23:
- More error return checks in upnpc.c
- #define MINIUPNPC_GET_SRC_ADDR enables receivedata() to get scope_id
- parseURL() now parses IPv6 addresses scope
- new parameter for miniwget() : IPv6 address scope
- increment API_VERSION to 9
-
-2012/06/20:
- fixed CMakeLists.txt
-
-2012/05/29
- Improvements in testminiwget.sh
-
-VERSION 1.7 : released 2012/05/24
-
-2012/05/01:
- Cleanup settings of CFLAGS in Makefile
- Fix signed/unsigned integer comparaisons
-
-2012/04/20:
- Allow to specify protocol with TCP or UDP for -A option
-
-2012/04/09:
- Only try to fetch XML description once in UPNP_GetValidIGD()
- Added -ansi flag to compilation, and fixed C++ comments to ANSI C comments.
-
-2012/04/05:
- minor improvements to minihttptestserver.c
-
-2012/03/15:
- upnperrors.c returns valid error string for unrecognized error codes
-
-2012/03/08:
- make minihttptestserver listen on loopback interface instead of 0.0.0.0
-
-2012/01/25:
- Maven installation thanks to Alexey Kuznetsov
-
-2012/01/21:
- Replace WIN32 macro by _WIN32
-
-2012/01/19:
- Fixes in java wrappers thanks to Alexey Kuznetsov :
- https://github.com/axet/miniupnp/tree/fix-javatest/miniupnpc
- Make and install .deb packages (python) thanks to Alexey Kuznetsov :
- https://github.com/axet/miniupnp/tree/feature-debbuild/miniupnpc
-
-2012/01/07:
- The multicast interface can now be specified by name with IPv4.
-
-2012/01/02:
- Install man page
-
-2011/11/25:
- added header to Port Mappings list in upnpc.c
-
-2011/10/09:
- Makefile : make clean now removes jnaerator generated files.
- MINIUPNPC_VERSION in miniupnpc.h (updated by make)
-
-2011/09/12:
- added rootdescURL to UPNPUrls structure.
-
-VERSION 1.6 : released 2011/07/25
-
-2011/07/25:
- Update doc for version 1.6 release
-
-2011/06/18:
- Fix for windows in miniwget.c
-
-2011/06/04:
- display remote host in port mapping listing
-
-2011/06/03:
- Fix in make install : there were missing headers
-
-2011/05/26:
- Fix the socket leak in miniwget thanks to Richard Marsh.
- Permit to add leaseduration in -a command. Display lease duration.
-
-2011/05/15:
- Try both LinkLocal and SiteLocal multicast address for SSDP in IPv6
-
-2011/05/09:
- add a test in testminiwget.sh.
- more error checking in miniwget.c
-
-2011/05/06:
- Adding some tool to test and validate miniwget.c
- simplified and debugged miniwget.c
-
-2011/04/11:
- moving ReceiveData() to a receivedata.c file.
- parsing presentation url
- adding IGD v2 WANIPv6FirewallControl commands
-
-2011/04/10:
- update of miniupnpcmodule.c
- comments in miniwget.c, update in testminiwget
- Adding errors codes from IGD v2
- new functions in upnpc.c for IGD v2
-
-2011/04/09:
- Support for litteral ip v6 address in miniwget
-
-2011/04/08:
- Adding support for urn:schemas-upnp-org:service:WANIPv6FirewallControl:1
- Updating APIVERSION
- Supporting IPV6 in upnpDiscover()
- Adding a -6 option to upnpc command line tool
-
-2011/03/18:
- miniwget/parseURL() : return an error when url param is null.
- fixing GetListOfPortMappings()
-
-2011/03/14:
- upnpDiscover() now reporting an error code.
- improvements in comments.
-
-2011/03/11:
- adding miniupnpcstrings.h.cmake and CMakeLists.txt files.
-
-2011/02/15:
- Implementation of GetListOfPortMappings()
-
-2011/02/07:
- updates to minixml to support character data starting with spaces
- minixml now support CDATA
- upnpreplyparse treats <NewPortListing> specificaly
- change in simpleUPnPcommand to return the buffer (simplification)
-
-2011/02/06:
- Added leaseDuration argument to AddPortMapping()
- Starting to implement GetListOfPortMappings()
-
-2011/01/11:
- updating wingenminiupnpcstrings.c
-
-2011/01/04:
- improving updateminiupnpcstrings.sh
-
-VERSION 1.5 : released 2011/01/01
-
-2010/12/21:
- use NO_GETADDRINFO macro to disable the use of getaddrinfo/freeaddrinfo
-
-2010/12/11:
- Improvements on getHTTPResponse() code.
-
-2010/12/09:
- new code for miniwget that handle Chunked transfer encoding
- using getHTTPResponse() in SOAP call code
- Adding MANIFEST.in for 'python setup.py bdist_rpm'
-
-2010/11/25:
- changes to minissdpc.c to compile under Win32.
- see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=729
-
-2010/09/17:
- Various improvement to Makefile from Michał Górny
-
-2010/08/05:
- Adding the script "external-ip.sh" from Reuben Hawkins
-
-2010/06/09:
- update to python module to match modification made on 2010/04/05
- update to Java test code to match modification made on 2010/04/05
- all UPNP_* function now return an error if the SOAP request failed
- at HTTP level.
-
-2010/04/17:
- Using GetBestRoute() under win32 in order to find the
- right interface to use.
-
-2010/04/12:
- Retrying with HTTP/1.1 if HTTP/1.0 failed. see
- http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1703
-
-2010/04/07:
- avoid returning duplicates in upnpDiscover()
-
-2010/04/05:
- Create a connecthostport.h/.c with connecthostport() function
- and use it in miniwget and miniupnpc.
- Use getnameinfo() instead of inet_ntop or inet_ntoa
- Work to make miniupnpc IPV6 compatible...
- Add java test code.
- Big changes in order to support device having both WANIPConnection
- and WANPPPConnection.
-
-2010/04/04:
- Use getaddrinfo() instead of gethostbyname() in miniwget.
-
-2010/01/06:
- #define _DARWIN_C_SOURCE for Mac OS X
-
-2009/12/19:
- Improve MinGW32 build
-
-2009/12/11:
- adding a MSVC9 project to build the static library and executable
-
-2009/12/10:
- Fixing some compilation stuff for Windows/MinGW
-
-2009/12/07:
- adaptations in Makefile and updateminiupnpcstring.sh for AmigaOS
- some fixes for Windows when using virtual ethernet adapters (it is the
- case with VMWare installed).
-
-2009/12/04:
- some fixes for AmigaOS compilation
- Changed HTTP version to HTTP/1.0 for Soap too (to prevent chunked
- transfer encoding)
-
-2009/12/03:
- updating printIDG and testigddescparse.c for debug.
- modifications to compile under AmigaOS
- adding a testminiwget program
- Changed miniwget to advertise itself as HTTP/1.0 to prevent chunked
- transfer encoding
-
-2009/11/26:
- fixing updateminiupnpcstrings.sh to take into account
- which command that does not return an error code.
-
-VERSION 1.4 : released 2009/10/30
-
-2009/10/16:
- using Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS in python module.
-
-2009/10/10:
- Some fixes for compilation under Solaris
- compilation fixes : http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1464
-
-2009/09/21:
- fixing the code to ignore EINTR during connect() calls.
-
-2009/08/07:
- Set socket timeout for connect()
- Some cleanup in miniwget.c
-
-2009/08/04:
- remove multiple redirections with -d in upnpc.c
- Print textual error code in upnpc.c
- Ignore EINTR during the connect() and poll() calls.
-
-2009/07/29:
- fix in updateminiupnpcstrings.sh if OS name contains "/"
- Sending a correct value for MX: field in SSDP request
-
-2009/07/20:
- Change the Makefile to compile under Mac OS X
- Fixed a stackoverflow in getDevicesFromMiniSSDPD()
-
-2009/07/09:
- Compile under Haiku
- generate miniupnpcstrings.h.in from miniupnpcstrings.h
-
-2009/06/04:
- patching to compile under CygWin and cross compile for minGW
-
-VERSION 1.3 :
-
-2009/04/17:
- updating python module
- Use strtoull() when using C99
-
-2009/02/28:
- Fixed miniwget.c for compiling under sun
-
-2008/12/18:
- cleanup in Makefile (thanks to Paul de Weerd)
- minissdpc.c : win32 compatibility
- miniupnpc.c : changed xmlns prefix from 'm' to 'u'
- Removed NDEBUG (using DEBUG)
-
-2008/10/14:
- Added the ExternalHost argument to DeletePortMapping()
-
-2008/10/11:
- Added the ExternalHost argument to AddPortMapping()
- Put a correct User-Agent: header in HTTP requests.
-
-VERSION 1.2 :
-
-2008/10/07:
- Update docs
-
-2008/09/25:
- Integrated sameport patch from Dario Meloni : Added a "sameport"
- argument to upnpDiscover().
-
-2008/07/18:
- small modif to make Clang happy :)
-
-2008/07/17:
- #define SOAPPREFIX "s" in miniupnpc.c in order to remove SOAP-ENV...
-
-2008/07/14:
- include declspec.h in installation (to /usr/include/miniupnpc)
-
-VERSION 1.1 :
-
-2008/07/04:
- standard options for install/ln instead of gnu-specific stuff.
-
-2008/07/03:
- now builds a .dll and .lib with win32. (mingw32)
-
-2008/04/28:
- make install now install the binary of the upnpc tool
-
-2008/04/27:
- added testupnpigd.py
- added error strings for miniupnpc "internal" errors
- improved python module error/exception reporting.
-
-2008/04/23:
- Completely rewrite igd_desc_parse.c in order to be compatible with
- Linksys WAG200G
- Added testigddescparse
- updated python module
-
-VERSION 1.0 :
-
-2008/02/21:
- put some #ifdef DEBUG around DisplayNameValueList()
-
-2008/02/18:
- Improved error reporting in upnpcommands.c
- UPNP_GetStatusInfo() returns LastConnectionError
-
-2008/02/16:
- better error handling in minisoap.c
- improving display of "valid IGD found" in upnpc.c
-
-2008/02/03:
- Fixing UPNP_GetValidIGD()
- improved make install :)
-
-2007/12/22:
- Adding upnperrors.c/h to provide a strupnperror() function
- used to translate UPnP error codes to string.
-
-2007/12/19:
- Fixing getDevicesFromMiniSSDPD()
- improved error reporting of UPnP functions
-
-2007/12/18:
- It is now possible to specify a different location for MiniSSDPd socket.
- working with MiniSSDPd is now more efficient.
- python module improved.
-
-2007/12/16:
- improving error reporting
-
-2007/12/13:
- Try to improve compatibility by using HTTP/1.0 instead of 1.1 and
- XML a bit different for SOAP.
-
-2007/11/25:
- fixed select() call for linux
-
-2007/11/15:
- Added -fPIC to CFLAG for better shared library code.
-
-2007/11/02:
- Fixed a potential socket leak in miniwget2()
-
-2007/10/16:
- added a parameter to upnpDiscover() in order to allow the use of another
- interface than the default multicast interface.
-
-2007/10/12:
- Fixed the creation of symbolic link in Makefile
-
-2007/10/08:
- Added man page
-
-2007/10/02:
- fixed memory bug in GetUPNPUrls()
-
-2007/10/01:
- fixes in the Makefile
- Added UPNP_GetIGDFromUrl() and adapted the sample program accordingly.
- Added SONAME in the shared library to please debian :)
- fixed MS Windows compilation (minissdpd is not available under MS Windows).
-
-2007/09/25:
- small change to Makefile to be able to install in a different location
- (default is /usr)
-
-2007/09/24:
- now compiling both shared and static library
-
-2007/09/19:
- Cosmetic changes on upnpc.c
-
-2007/09/02:
- adapting to new miniSSDPd (release version ?)
-
-2007/08/31:
- Usage of miniSSDPd to skip discovery process.
-
-2007/08/27:
- fixed python module to allow compilation with Python older than Python 2.4
-
-2007/06/12:
- Added a python module.
-
-2007/05/19:
- Fixed compilation under MinGW
-
-2007/05/15:
- fixed a memory leak in AddPortMapping()
- Added testupnpreplyparse executable to check the parsing of
- upnp soap messages
- minixml now ignore namespace prefixes.
-
-2007/04/26:
- upnpc now displays external ip address with -s or -l
-
-2007/04/11:
- changed MINIUPNPC_URL_MAXSIZE to 128 to accomodate the "BT Voyager 210"
-
-2007/03/19:
- cleanup in miniwget.c
-
-2007/03/01:
- Small typo fix...
-
-2007/01/30:
- Now parsing the HTTP header from SOAP responses in order to
- get content-length value.
-
-2007/01/29:
- Fixed the Soap Query to speedup the HTTP request.
- added some Win32 DLL stuff...
-
-2007/01/27:
- Fixed some WIN32 compatibility issues
-
-2006/12/14:
- Added UPNPIGD_IsConnected() function in miniupnp.c/.h
- Added UPNP_GetValidIGD() in miniupnp.c/.h
- cleaned upnpc.c main(). now using UPNP_GetValidIGD()
-
-2006/12/07:
- Version 1.0-RC1 released
-
-2006/12/03:
- Minor changes to compile under SunOS/Solaris
-
-2006/11/30:
- made a minixml parser validator program
- updated minixml to handle attributes correctly
-
-2006/11/22:
- Added a -r option to the upnpc sample thanks to Alexander Hubmann.
-
-2006/11/19:
- Cleanup code to make it more ANSI C compliant
-
-2006/11/10:
- detect and display local lan address.
-
-2006/11/04:
- Packets and Bytes Sent/Received are now unsigned int.
-
-2006/11/01:
- Bug fix thanks to Giuseppe D'Angelo
-
-2006/10/31:
- C++ compatibility for .h files.
- Added a way to get ip Address on the LAN used to reach the IGD.
-
-2006/10/25:
- Added M-SEARCH to the services in the discovery process.
-
-2006/10/22:
- updated the Makefile to use makedepend, added a "make install"
- update Makefile
-
-2006/10/20:
- fixing the description url parsing thanks to patch sent by
- Wayne Dawe.
- Fixed/translated some comments.
- Implemented a better discover process, first looking
- for IGD then for root devices (as some devices only reply to
- M-SEARCH for root devices).
-
-2006/09/02:
- added freeUPNPDevlist() function.
-
-2006/08/04:
- More command line arguments checking
-
-2006/08/01:
- Added the .bat file to compile under Win32 with minGW32
-
-2006/07/31:
- Fixed the rootdesc parser (igd_desc_parse.c)
-
-2006/07/20:
- parseMSEARCHReply() is now returning the ST: line as well
- starting changes to detect several UPnP devices on the network
-
-2006/07/19:
- using GetCommonLinkProperties to get down/upload bitrate
-
+++ /dev/null
-MiniUPnPc
-Copyright (c) 2005-2011, Thomas BERNARD
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
+++ /dev/null
-noinst_LIBRARIES = libminiupnp.a
-
-AM_CFLAGS = @PTHREAD_CFLAGS@ -DNDEBUG -D_GNU_SOURCE
-
-libminiupnp_a_SOURCES = \
- connecthostport.c \
- igd_desc_parse.c \
- minisoap.c \
- minissdpc.c \
- miniupnpc.c \
- miniwget.c \
- minixml.c \
- portlistingparse.c \
- receivedata.c \
- upnpcommands.c \
- upnpreplyparse.c
-
-noinst_HEADERS = \
- bsdqueue.h \
- codelength.h \
- connecthostport.h \
- declspec.h \
- igd_desc_parse.h \
- minisoap.h \
- minissdpc.h \
- miniupnpc.h \
- miniupnpctypes.h \
- miniwget.h \
- minixml.h \
- portlistingparse.h \
- receivedata.h \
- upnpcommands.h \
- upnpreplyparse.h
-
-EXTRA_DIST = \
- README \
- LICENSE \
- miniupnpcstrings.h.in \
- updateminiupnpcstrings.sh
-
-BUILT_SOURCES = \
- miniupnpcstrings.h
-
-miniupnpcstrings.h: Makefile
- $(srcdir)/updateminiupnpcstrings.sh $(srcdir)/VERSION $(srcdir)/miniupnpcstrings.h.in $@
-
-DISTCLEANFILES = $(builddir)/miniupnpcstrings.h
+++ /dev/null
-Project: miniupnp
-Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
-github: https://github.com/miniupnp/miniupnp
-freecode: http://freecode.com/projects/miniupnp
-Author: Thomas Bernard
-Copyright (c) 2005-2012 Thomas Bernard
-This software is subject to the conditions detailed in the
-LICENSE file provided within this distribution.
-
-
-For the comfort of Win32 users, bsdqueue.h is included in the distribution.
-Its licence is included in the header of the file.
-bsdqueue.h is a copy of the sys/queue.h of an OpenBSD system.
-
-
-* miniUPnP Client - miniUPnPc *
-
-To compile, simply run 'gmake' (could be 'make' on your system).
-Under win32, to compile with MinGW, type "mingw32make.bat".
-MS Visual C solution and project files are supplied in the msvc/ subdirectory.
-
-The compilation is known to work under linux, FreeBSD,
-OpenBSD, MacOS X, AmigaOS and cygwin.
-The official AmigaOS4.1 SDK was used for AmigaOS4 and GeekGadgets for AmigaOS3.
-upx (http://upx.sourceforge.net) is used to compress the win32 .exe files.
-
-To install the library and headers on the system use :
-> su
-> make install
-> exit
-
-alternatively, to install into a specific location, use :
-> INSTALLPREFIX=/usr/local make install
-
-upnpc.c is a sample client using the libminiupnpc.
-To use the libminiupnpc in your application, link it with
-libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h,
-upnpcommands.h and miniwget.h :
-- upnpDiscover()
-- miniwget()
-- parserootdesc()
-- GetUPNPUrls()
-- UPNP_* (calling UPNP methods)
-
-Note : use #include <miniupnpc/miniupnpc.h> etc... for the includes
-and -lminiupnpc for the link
-
-Discovery process is speeded up when MiniSSDPd is running on the machine.
-
-
-* Python module *
-
-you can build a python module with 'make pythonmodule'
-and install it with 'make installpythonmodule'.
-setup.py (and setupmingw32.py) are included in the distribution.
-
-
-Feel free to contact me if you have any problem :
-e-mail : miniupnp@free.fr
-
-If you are using libminiupnpc in your application, please
-send me an email !
-
-For any question, you can use the web forum :
-http://miniupnp.tuxfamily.org/forum/
-
+++ /dev/null
-$Id: apiversions.txt,v 1.3 2014/01/31 13:14:32 nanard Exp $
-
-Differences in API between miniUPnPc versions
-
-====================== miniUPnPc version 1.9 ======================
-API version 10
-
-upnpcommands.h:
- added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
-
-miniupnpc.h:
- updated macros :
- #define MINIUPNPC_VERSION "1.9"
- #define MINIUPNPC_API_VERSION 10
-
-====================== miniUPnPc version 1.8 ======================
-API version 9
-
-miniupnpc.h:
- updated macros :
- #define MINIUPNPC_VERSION "1.8"
- #define MINIUPNPC_API_VERSION 9
- added "unsigned int scope_id;" to struct UPNPDev
- added scope_id argument to GetUPNPUrls()
-
-
-
-====================== miniUPnPc version 1.7 ======================
-API version 8
-
-miniupnpc.h :
- add new macros :
- #define MINIUPNPC_VERSION "1.7"
- #define MINIUPNPC_API_VERSION 8
- add rootdescURL to struct UPNPUrls
-
-
-
-====================== miniUPnPc version 1.6 ======================
-API version 8
-
-Adding support for IPv6.
-igd_desc_parse.h :
- struct IGDdatas_service :
- add char presentationurl[MINIUPNPC_URL_MAXSIZE];
- struct IGDdatas :
- add struct IGDdatas_service IPv6FC;
-miniupnpc.h :
- new macros :
- #define UPNPDISCOVER_SUCCESS (0)
- #define UPNPDISCOVER_UNKNOWN_ERROR (-1)
- #define UPNPDISCOVER_SOCKET_ERROR (-101)
- #define UPNPDISCOVER_MEMORY_ERROR (-102)
- simpleUPnPcommand() prototype changed (but is normaly not used by API users)
- add arguments ipv6 and error to upnpDiscover() :
- struct UPNPDev *
- upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock, int sameport,
- int ipv6,
- int * error);
- add controlURL_6FC member to struct UPNPUrls :
- struct UPNPUrls {
- char * controlURL;
- char * ipcondescURL;
- char * controlURL_CIF;
- char * controlURL_6FC;
- };
-
-upnpcommands.h :
- add leaseDuration argument to UPNP_AddPortMapping()
- add desc, enabled and leaseDuration arguments to UPNP_GetSpecificPortMappingEntry()
- add UPNP_GetListOfPortMappings() function (IGDv2)
- add IGDv2 IPv6 related functions :
- UPNP_GetFirewallStatus()
- UPNP_GetOutboundPinholeTimeout()
- UPNP_AddPinhole()
- UPNP_UpdatePinhole()
- UPNP_DeletePinhole()
- UPNP_CheckPinholeWorking()
- UPNP_GetPinholePackets()
-
-
-
-====================== miniUPnPc version 1.5 ======================
-API version 5
-
-new function :
-int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
-new macro in upnpcommands.h :
-#define UPNPCOMMAND_HTTP_ERROR
-
-====================== miniUPnPc version 1.4 ======================
-Same API as version 1.3
-
-====================== miniUPnPc version 1.3 ======================
-API version 4
-
-Use UNSIGNED_INTEGER type for
-UPNP_GetTotalBytesSent(), UPNP_GetTotalBytesReceived(),
-UPNP_GetTotalPacketsSent(), UPNP_GetTotalPacketsReceived()
-Add remoteHost argument to UPNP_AddPortMapping() and UPNP_DeletePortMapping()
-
-====================== miniUPnPc version 1.2 ======================
-API version 3
-
-added sameport argument to upnpDiscover()
-struct UPNPDev *
-upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock, int sameport);
-
-====================== miniUPnPc Version 1.1 ======================
-Same API as 1.0
-
-
-====================== miniUPnPc Version 1.0 ======================
-API version 2
-
-
-struct UPNPDev {
- struct UPNPDev * pNext;
- char * descURL;
- char * st;
- char buffer[2];
-};
-struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock);
-
+++ /dev/null
-/* $OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $ */
-/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
-
-/*
- * Copyright (c) 1991, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)queue.h 8.5 (Berkeley) 8/20/94
- */
-
-#ifndef _SYS_QUEUE_H_
-#define _SYS_QUEUE_H_
-
-/*
- * This file defines five types of data structures: singly-linked lists,
- * lists, simple queues, tail queues, and circular queues.
- *
- *
- * A singly-linked list is headed by a single forward pointer. The elements
- * are singly linked for minimum space and pointer manipulation overhead at
- * the expense of O(n) removal for arbitrary elements. New elements can be
- * added to the list after an existing element or at the head of the list.
- * Elements being removed from the head of the list should use the explicit
- * macro for this purpose for optimum efficiency. A singly-linked list may
- * only be traversed in the forward direction. Singly-linked lists are ideal
- * for applications with large datasets and few or no removals or for
- * implementing a LIFO queue.
- *
- * A list is headed by a single forward pointer (or an array of forward
- * pointers for a hash table header). The elements are doubly linked
- * so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before
- * or after an existing element or at the head of the list. A list
- * may only be traversed in the forward direction.
- *
- * A simple queue is headed by a pair of pointers, one the head of the
- * list and the other to the tail of the list. The elements are singly
- * linked to save space, so elements can only be removed from the
- * head of the list. New elements can be added to the list before or after
- * an existing element, at the head of the list, or at the end of the
- * list. A simple queue may only be traversed in the forward direction.
- *
- * A tail queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or
- * after an existing element, at the head of the list, or at the end of
- * the list. A tail queue may be traversed in either direction.
- *
- * A circle queue is headed by a pair of pointers, one to the head of the
- * list and the other to the tail of the list. The elements are doubly
- * linked so that an arbitrary element can be removed without a need to
- * traverse the list. New elements can be added to the list before or after
- * an existing element, at the head of the list, or at the end of the list.
- * A circle queue may be traversed in either direction, but has a more
- * complex end of list detection.
- *
- * For details on the use of these macros, see the queue(3) manual page.
- */
-
-#ifdef QUEUE_MACRO_DEBUG
-#define _Q_INVALIDATE(a) (a) = ((void *)-1)
-#else
-#define _Q_INVALIDATE(a)
-#endif
-
-/*
- * Singly-linked List definitions.
- */
-#define SLIST_HEAD(name, type) \
-struct name { \
- struct type *slh_first; /* first element */ \
-}
-
-#define SLIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#ifdef SLIST_ENTRY
-#undef SLIST_ENTRY
-#endif
-
-#define SLIST_ENTRY(type) \
-struct { \
- struct type *sle_next; /* next element */ \
-}
-
-/*
- * Singly-linked List access methods.
- */
-#define SLIST_FIRST(head) ((head)->slh_first)
-#define SLIST_END(head) NULL
-#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
-#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
-
-#define SLIST_FOREACH(var, head, field) \
- for((var) = SLIST_FIRST(head); \
- (var) != SLIST_END(head); \
- (var) = SLIST_NEXT(var, field))
-
-#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
- for ((varp) = &SLIST_FIRST((head)); \
- ((var) = *(varp)) != SLIST_END(head); \
- (varp) = &SLIST_NEXT((var), field))
-
-/*
- * Singly-linked List functions.
- */
-#define SLIST_INIT(head) { \
- SLIST_FIRST(head) = SLIST_END(head); \
-}
-
-#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
- (elm)->field.sle_next = (slistelm)->field.sle_next; \
- (slistelm)->field.sle_next = (elm); \
-} while (0)
-
-#define SLIST_INSERT_HEAD(head, elm, field) do { \
- (elm)->field.sle_next = (head)->slh_first; \
- (head)->slh_first = (elm); \
-} while (0)
-
-#define SLIST_REMOVE_NEXT(head, elm, field) do { \
- (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
-} while (0)
-
-#define SLIST_REMOVE_HEAD(head, field) do { \
- (head)->slh_first = (head)->slh_first->field.sle_next; \
-} while (0)
-
-#define SLIST_REMOVE(head, elm, type, field) do { \
- if ((head)->slh_first == (elm)) { \
- SLIST_REMOVE_HEAD((head), field); \
- } else { \
- struct type *curelm = (head)->slh_first; \
- \
- while (curelm->field.sle_next != (elm)) \
- curelm = curelm->field.sle_next; \
- curelm->field.sle_next = \
- curelm->field.sle_next->field.sle_next; \
- _Q_INVALIDATE((elm)->field.sle_next); \
- } \
-} while (0)
-
-/*
- * List definitions.
- */
-#define LIST_HEAD(name, type) \
-struct name { \
- struct type *lh_first; /* first element */ \
-}
-
-#define LIST_HEAD_INITIALIZER(head) \
- { NULL }
-
-#define LIST_ENTRY(type) \
-struct { \
- struct type *le_next; /* next element */ \
- struct type **le_prev; /* address of previous next element */ \
-}
-
-/*
- * List access methods
- */
-#define LIST_FIRST(head) ((head)->lh_first)
-#define LIST_END(head) NULL
-#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
-#define LIST_NEXT(elm, field) ((elm)->field.le_next)
-
-#define LIST_FOREACH(var, head, field) \
- for((var) = LIST_FIRST(head); \
- (var)!= LIST_END(head); \
- (var) = LIST_NEXT(var, field))
-
-/*
- * List functions.
- */
-#define LIST_INIT(head) do { \
- LIST_FIRST(head) = LIST_END(head); \
-} while (0)
-
-#define LIST_INSERT_AFTER(listelm, elm, field) do { \
- if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
- (listelm)->field.le_next->field.le_prev = \
- &(elm)->field.le_next; \
- (listelm)->field.le_next = (elm); \
- (elm)->field.le_prev = &(listelm)->field.le_next; \
-} while (0)
-
-#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
- (elm)->field.le_prev = (listelm)->field.le_prev; \
- (elm)->field.le_next = (listelm); \
- *(listelm)->field.le_prev = (elm); \
- (listelm)->field.le_prev = &(elm)->field.le_next; \
-} while (0)
-
-#define LIST_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.le_next = (head)->lh_first) != NULL) \
- (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
- (head)->lh_first = (elm); \
- (elm)->field.le_prev = &(head)->lh_first; \
-} while (0)
-
-#define LIST_REMOVE(elm, field) do { \
- if ((elm)->field.le_next != NULL) \
- (elm)->field.le_next->field.le_prev = \
- (elm)->field.le_prev; \
- *(elm)->field.le_prev = (elm)->field.le_next; \
- _Q_INVALIDATE((elm)->field.le_prev); \
- _Q_INVALIDATE((elm)->field.le_next); \
-} while (0)
-
-#define LIST_REPLACE(elm, elm2, field) do { \
- if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
- (elm2)->field.le_next->field.le_prev = \
- &(elm2)->field.le_next; \
- (elm2)->field.le_prev = (elm)->field.le_prev; \
- *(elm2)->field.le_prev = (elm2); \
- _Q_INVALIDATE((elm)->field.le_prev); \
- _Q_INVALIDATE((elm)->field.le_next); \
-} while (0)
-
-/*
- * Simple queue definitions.
- */
-#define SIMPLEQ_HEAD(name, type) \
-struct name { \
- struct type *sqh_first; /* first element */ \
- struct type **sqh_last; /* addr of last next element */ \
-}
-
-#define SIMPLEQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).sqh_first }
-
-#define SIMPLEQ_ENTRY(type) \
-struct { \
- struct type *sqe_next; /* next element */ \
-}
-
-/*
- * Simple queue access methods.
- */
-#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
-#define SIMPLEQ_END(head) NULL
-#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
-#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
-
-#define SIMPLEQ_FOREACH(var, head, field) \
- for((var) = SIMPLEQ_FIRST(head); \
- (var) != SIMPLEQ_END(head); \
- (var) = SIMPLEQ_NEXT(var, field))
-
-/*
- * Simple queue functions.
- */
-#define SIMPLEQ_INIT(head) do { \
- (head)->sqh_first = NULL; \
- (head)->sqh_last = &(head)->sqh_first; \
-} while (0)
-
-#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
- (head)->sqh_last = &(elm)->field.sqe_next; \
- (head)->sqh_first = (elm); \
-} while (0)
-
-#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.sqe_next = NULL; \
- *(head)->sqh_last = (elm); \
- (head)->sqh_last = &(elm)->field.sqe_next; \
-} while (0)
-
-#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
- if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
- (head)->sqh_last = &(elm)->field.sqe_next; \
- (listelm)->field.sqe_next = (elm); \
-} while (0)
-
-#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
- if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
- (head)->sqh_last = &(head)->sqh_first; \
-} while (0)
-
-/*
- * Tail queue definitions.
- */
-#define TAILQ_HEAD(name, type) \
-struct name { \
- struct type *tqh_first; /* first element */ \
- struct type **tqh_last; /* addr of last next element */ \
-}
-
-#define TAILQ_HEAD_INITIALIZER(head) \
- { NULL, &(head).tqh_first }
-
-#define TAILQ_ENTRY(type) \
-struct { \
- struct type *tqe_next; /* next element */ \
- struct type **tqe_prev; /* address of previous next element */ \
-}
-
-/*
- * tail queue access methods
- */
-#define TAILQ_FIRST(head) ((head)->tqh_first)
-#define TAILQ_END(head) NULL
-#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-#define TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
-/* XXX */
-#define TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-#define TAILQ_EMPTY(head) \
- (TAILQ_FIRST(head) == TAILQ_END(head))
-
-#define TAILQ_FOREACH(var, head, field) \
- for((var) = TAILQ_FIRST(head); \
- (var) != TAILQ_END(head); \
- (var) = TAILQ_NEXT(var, field))
-
-#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
- for((var) = TAILQ_LAST(head, headname); \
- (var) != TAILQ_END(head); \
- (var) = TAILQ_PREV(var, headname, field))
-
-/*
- * Tail queue functions.
- */
-#define TAILQ_INIT(head) do { \
- (head)->tqh_first = NULL; \
- (head)->tqh_last = &(head)->tqh_first; \
-} while (0)
-
-#define TAILQ_INSERT_HEAD(head, elm, field) do { \
- if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
- (head)->tqh_first->field.tqe_prev = \
- &(elm)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm)->field.tqe_next; \
- (head)->tqh_first = (elm); \
- (elm)->field.tqe_prev = &(head)->tqh_first; \
-} while (0)
-
-#define TAILQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.tqe_next = NULL; \
- (elm)->field.tqe_prev = (head)->tqh_last; \
- *(head)->tqh_last = (elm); \
- (head)->tqh_last = &(elm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
- if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
- (elm)->field.tqe_next->field.tqe_prev = \
- &(elm)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm)->field.tqe_next; \
- (listelm)->field.tqe_next = (elm); \
- (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
- (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
- (elm)->field.tqe_next = (listelm); \
- *(listelm)->field.tqe_prev = (elm); \
- (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
-} while (0)
-
-#define TAILQ_REMOVE(head, elm, field) do { \
- if (((elm)->field.tqe_next) != NULL) \
- (elm)->field.tqe_next->field.tqe_prev = \
- (elm)->field.tqe_prev; \
- else \
- (head)->tqh_last = (elm)->field.tqe_prev; \
- *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
- _Q_INVALIDATE((elm)->field.tqe_prev); \
- _Q_INVALIDATE((elm)->field.tqe_next); \
-} while (0)
-
-#define TAILQ_REPLACE(head, elm, elm2, field) do { \
- if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
- (elm2)->field.tqe_next->field.tqe_prev = \
- &(elm2)->field.tqe_next; \
- else \
- (head)->tqh_last = &(elm2)->field.tqe_next; \
- (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
- *(elm2)->field.tqe_prev = (elm2); \
- _Q_INVALIDATE((elm)->field.tqe_prev); \
- _Q_INVALIDATE((elm)->field.tqe_next); \
-} while (0)
-
-/*
- * Circular queue definitions.
- */
-#define CIRCLEQ_HEAD(name, type) \
-struct name { \
- struct type *cqh_first; /* first element */ \
- struct type *cqh_last; /* last element */ \
-}
-
-#define CIRCLEQ_HEAD_INITIALIZER(head) \
- { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
-
-#define CIRCLEQ_ENTRY(type) \
-struct { \
- struct type *cqe_next; /* next element */ \
- struct type *cqe_prev; /* previous element */ \
-}
-
-/*
- * Circular queue access methods
- */
-#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
-#define CIRCLEQ_LAST(head) ((head)->cqh_last)
-#define CIRCLEQ_END(head) ((void *)(head))
-#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
-#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
-#define CIRCLEQ_EMPTY(head) \
- (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
-
-#define CIRCLEQ_FOREACH(var, head, field) \
- for((var) = CIRCLEQ_FIRST(head); \
- (var) != CIRCLEQ_END(head); \
- (var) = CIRCLEQ_NEXT(var, field))
-
-#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
- for((var) = CIRCLEQ_LAST(head); \
- (var) != CIRCLEQ_END(head); \
- (var) = CIRCLEQ_PREV(var, field))
-
-/*
- * Circular queue functions.
- */
-#define CIRCLEQ_INIT(head) do { \
- (head)->cqh_first = CIRCLEQ_END(head); \
- (head)->cqh_last = CIRCLEQ_END(head); \
-} while (0)
-
-#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
- (elm)->field.cqe_next = (listelm)->field.cqe_next; \
- (elm)->field.cqe_prev = (listelm); \
- if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm); \
- else \
- (listelm)->field.cqe_next->field.cqe_prev = (elm); \
- (listelm)->field.cqe_next = (elm); \
-} while (0)
-
-#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
- (elm)->field.cqe_next = (listelm); \
- (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
- if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm); \
- else \
- (listelm)->field.cqe_prev->field.cqe_next = (elm); \
- (listelm)->field.cqe_prev = (elm); \
-} while (0)
-
-#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
- (elm)->field.cqe_next = (head)->cqh_first; \
- (elm)->field.cqe_prev = CIRCLEQ_END(head); \
- if ((head)->cqh_last == CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm); \
- else \
- (head)->cqh_first->field.cqe_prev = (elm); \
- (head)->cqh_first = (elm); \
-} while (0)
-
-#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
- (elm)->field.cqe_next = CIRCLEQ_END(head); \
- (elm)->field.cqe_prev = (head)->cqh_last; \
- if ((head)->cqh_first == CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm); \
- else \
- (head)->cqh_last->field.cqe_next = (elm); \
- (head)->cqh_last = (elm); \
-} while (0)
-
-#define CIRCLEQ_REMOVE(head, elm, field) do { \
- if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
- (head)->cqh_last = (elm)->field.cqe_prev; \
- else \
- (elm)->field.cqe_next->field.cqe_prev = \
- (elm)->field.cqe_prev; \
- if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
- (head)->cqh_first = (elm)->field.cqe_next; \
- else \
- (elm)->field.cqe_prev->field.cqe_next = \
- (elm)->field.cqe_next; \
- _Q_INVALIDATE((elm)->field.cqe_prev); \
- _Q_INVALIDATE((elm)->field.cqe_next); \
-} while (0)
-
-#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
- if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
- CIRCLEQ_END(head)) \
- (head).cqh_last = (elm2); \
- else \
- (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
- if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
- CIRCLEQ_END(head)) \
- (head).cqh_first = (elm2); \
- else \
- (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
- _Q_INVALIDATE((elm)->field.cqe_prev); \
- _Q_INVALIDATE((elm)->field.cqe_next); \
-} while (0)
-
-#endif /* !_SYS_QUEUE_H_ */
+++ /dev/null
-/* $Id: codelength.h,v 1.4 2012/09/27 15:40:29 nanard Exp $ */
-/* Project : miniupnp
- * Author : Thomas BERNARD
- * copyright (c) 2005-2011 Thomas Bernard
- * This software is subjet to the conditions detailed in the
- * provided LICENCE file. */
-#ifndef CODELENGTH_H_INCLUDED
-#define CODELENGTH_H_INCLUDED
-
-/* Encode length by using 7bit per Byte :
- * Most significant bit of each byte specifies that the
- * following byte is part of the code */
-#define DECODELENGTH(n, p) n = 0; \
- do { n = (n << 7) | (*p & 0x7f); } \
- while((*(p++)&0x80) && (n<(1<<25)));
-
-#define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \
- n = 0; \
- do { \
- if((p) >= (p_limit)) break; \
- n = (n << 7) | (*(p) & 0x7f); \
- } while((*((p)++)&0x80) && (n<(1<<25)));
-
-#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
- if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
- if(n>=16384) *(p++) = (n >> 14) | 0x80; \
- if(n>=128) *(p++) = (n >> 7) | 0x80; \
- *(p++) = n & 0x7f;
-
-#endif
-
+++ /dev/null
-/* $Id: connecthostport.c,v 1.13 2014/03/31 12:36:36 nanard Exp $ */
-/* Project : miniupnp
- * Author : Thomas Bernard
- * Copyright (c) 2010-2014 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution. */
-
-/* use getaddrinfo() or gethostbyname()
- * uncomment the following line in order to use gethostbyname() */
-#ifdef NO_GETADDRINFO
-#define USE_GETHOSTBYNAME
-#endif
-
-#include <string.h>
-#include <stdio.h>
-#ifdef _WIN32
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <io.h>
-#define MAXHOSTNAMELEN 64
-#define snprintf _snprintf
-#define herror
-#define socklen_t int
-#else /* #ifdef _WIN32 */
-#include <unistd.h>
-#include <sys/param.h>
-#include <sys/select.h>
-#include <errno.h>
-#define closesocket close
-#include <netdb.h>
-#include <netinet/in.h>
-/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
- * during the connect() call */
-#define MINIUPNPC_IGNORE_EINTR
-#ifndef USE_GETHOSTBYNAME
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#endif /* #ifndef USE_GETHOSTBYNAME */
-#endif /* #else _WIN32 */
-
-/* definition of PRINT_SOCKET_ERROR */
-#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
-#else
-#define PRINT_SOCKET_ERROR(x) perror(x)
-#endif
-
-#if defined(__amigaos__) || defined(__amigaos4__)
-#define herror(A) printf("%s\n", A)
-#endif
-
-#include "connecthostport.h"
-
-#ifndef MAXHOSTNAMELEN
-#define MAXHOSTNAMELEN 64
-#endif
-
-/* connecthostport()
- * return a socket connected (TCP) to the host and port
- * or -1 in case of error */
-int connecthostport(const char * host, unsigned short port,
- unsigned int scope_id)
-{
- int s, n;
-#ifdef USE_GETHOSTBYNAME
- struct sockaddr_in dest;
- struct hostent *hp;
-#else /* #ifdef USE_GETHOSTBYNAME */
- char tmp_host[MAXHOSTNAMELEN+1];
- char port_str[8];
- struct addrinfo *ai, *p;
- struct addrinfo hints;
-#endif /* #ifdef USE_GETHOSTBYNAME */
-#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
- struct timeval timeout;
-#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
-
-#ifdef USE_GETHOSTBYNAME
- hp = gethostbyname(host);
- if(hp == NULL)
- {
- herror(host);
- return -1;
- }
- memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
- memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
- s = socket(PF_INET, SOCK_STREAM, 0);
- if(s < 0)
- {
- PRINT_SOCKET_ERROR("socket");
- return -1;
- }
-#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
- /* setting a 3 seconds timeout for the connect() call */
- timeout.tv_sec = 3;
- timeout.tv_usec = 0;
- if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
- {
- PRINT_SOCKET_ERROR("setsockopt");
- }
- timeout.tv_sec = 3;
- timeout.tv_usec = 0;
- if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
- {
- PRINT_SOCKET_ERROR("setsockopt");
- }
-#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
- dest.sin_family = AF_INET;
- dest.sin_port = htons(port);
- n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
-#ifdef MINIUPNPC_IGNORE_EINTR
- /* EINTR The system call was interrupted by a signal that was caught
- * EINPROGRESS The socket is nonblocking and the connection cannot
- * be completed immediately. */
- while(n < 0 && (errno == EINTR || errno = EINPROGRESS))
- {
- socklen_t len;
- fd_set wset;
- int err;
- FD_ZERO(&wset);
- FD_SET(s, &wset);
- if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
- continue;
- /*len = 0;*/
- /*n = getpeername(s, NULL, &len);*/
- len = sizeof(err);
- if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
- PRINT_SOCKET_ERROR("getsockopt");
- closesocket(s);
- return -1;
- }
- if(err != 0) {
- errno = err;
- n = -1;
- }
- }
-#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
- if(n<0)
- {
- PRINT_SOCKET_ERROR("connect");
- closesocket(s);
- return -1;
- }
-#else /* #ifdef USE_GETHOSTBYNAME */
- /* use getaddrinfo() instead of gethostbyname() */
- memset(&hints, 0, sizeof(hints));
- /* hints.ai_flags = AI_ADDRCONFIG; */
-#ifdef AI_NUMERICSERV
- hints.ai_flags = AI_NUMERICSERV;
-#endif
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
- /* hints.ai_protocol = IPPROTO_TCP; */
- snprintf(port_str, sizeof(port_str), "%hu", port);
- if(host[0] == '[')
- {
- /* literal ip v6 address */
- int i, j;
- for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++)
- {
- tmp_host[i] = host[j];
- if(0 == memcmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */
- j+=2; /* skip "25" */
- }
- tmp_host[i] = '\0';
- }
- else
- {
- strncpy(tmp_host, host, MAXHOSTNAMELEN);
- }
- tmp_host[MAXHOSTNAMELEN] = '\0';
- n = getaddrinfo(tmp_host, port_str, &hints, &ai);
- if(n != 0)
- {
-#ifdef _WIN32
- fprintf(stderr, "getaddrinfo() error : %d\n", n);
-#else
- fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
-#endif
- return -1;
- }
- s = -1;
- for(p = ai; p; p = p->ai_next)
- {
- s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
- if(s < 0)
- continue;
- if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) {
- struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr;
- addr6->sin6_scope_id = scope_id;
- }
-#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
- /* setting a 3 seconds timeout for the connect() call */
- timeout.tv_sec = 3;
- timeout.tv_usec = 0;
- if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
- {
- PRINT_SOCKET_ERROR("setsockopt");
- }
- timeout.tv_sec = 3;
- timeout.tv_usec = 0;
- if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
- {
- PRINT_SOCKET_ERROR("setsockopt");
- }
-#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
- n = connect(s, p->ai_addr, p->ai_addrlen);
-#ifdef MINIUPNPC_IGNORE_EINTR
- /* EINTR The system call was interrupted by a signal that was caught
- * EINPROGRESS The socket is nonblocking and the connection cannot
- * be completed immediately. */
- while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
- {
- socklen_t len;
- fd_set wset;
- int err;
- FD_ZERO(&wset);
- FD_SET(s, &wset);
- if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
- continue;
- /*len = 0;*/
- /*n = getpeername(s, NULL, &len);*/
- len = sizeof(err);
- if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
- PRINT_SOCKET_ERROR("getsockopt");
- closesocket(s);
- freeaddrinfo(ai);
- return -1;
- }
- if(err != 0) {
- errno = err;
- n = -1;
- }
- }
-#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
- if(n < 0)
- {
- closesocket(s);
- continue;
- }
- else
- {
- break;
- }
- }
- freeaddrinfo(ai);
- if(s < 0)
- {
- PRINT_SOCKET_ERROR("socket");
- return -1;
- }
- if(n < 0)
- {
- PRINT_SOCKET_ERROR("connect");
- return -1;
- }
-#endif /* #ifdef USE_GETHOSTBYNAME */
- return s;
-}
-
+++ /dev/null
-/* $Id: connecthostport.h,v 1.3 2012/09/27 15:42:10 nanard Exp $ */
-/* Project: miniupnp
- * http://miniupnp.free.fr/
- * Author: Thomas Bernard
- * Copyright (c) 2010-2012 Thomas Bernard
- * This software is subjects to the conditions detailed
- * in the LICENCE file provided within this distribution */
-#ifndef CONNECTHOSTPORT_H_INCLUDED
-#define CONNECTHOSTPORT_H_INCLUDED
-
-/* connecthostport()
- * return a socket connected (TCP) to the host and port
- * or -1 in case of error */
-int connecthostport(const char * host, unsigned short port,
- unsigned int scope_id);
-
-#endif
-
+++ /dev/null
-#ifndef DECLSPEC_H_INCLUDED
-#define DECLSPEC_H_INCLUDED
-
-#if defined(_WIN32) && !defined(STATICLIB)
- /* for windows dll */
- #ifdef MINIUPNP_EXPORTS
- #define LIBSPEC __declspec(dllexport)
- #else
- #define LIBSPEC __declspec(dllimport)
- #endif
-#else
- #if defined(__GNUC__) && __GNUC__ >= 4
- /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */
- #define LIBSPEC __attribute__ ((visibility ("default")))
- #else
- #define LIBSPEC
- #endif
-#endif
-
-#endif
-
+++ /dev/null
-/* $Id: igd_desc_parse.c,v 1.14 2011/04/11 09:19:24 nanard Exp $ */
-/* Project : miniupnp
- * http://miniupnp.free.fr/
- * Author : Thomas Bernard
- * Copyright (c) 2005-2010 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution. */
-
-#include "igd_desc_parse.h"
-#include <stdio.h>
-#include <string.h>
-
-/* Start element handler :
- * update nesting level counter and copy element name */
-void IGDstartelt(void * d, const char * name, int l)
-{
- struct IGDdatas * datas = (struct IGDdatas *)d;
- memcpy( datas->cureltname, name, l);
- datas->cureltname[l] = '\0';
- datas->level++;
- if( (l==7) && !memcmp(name, "service", l) ) {
- datas->tmp.controlurl[0] = '\0';
- datas->tmp.eventsuburl[0] = '\0';
- datas->tmp.scpdurl[0] = '\0';
- datas->tmp.servicetype[0] = '\0';
- }
-}
-
-/* End element handler :
- * update nesting level counter and update parser state if
- * service element is parsed */
-void IGDendelt(void * d, const char * name, int l)
-{
- struct IGDdatas * datas = (struct IGDdatas *)d;
- datas->level--;
- /*printf("endelt %2d %.*s\n", datas->level, l, name);*/
- if( (l==7) && !memcmp(name, "service", l) )
- {
- /*
- if( datas->state < 1
- && !strcmp(datas->servicetype,
- // "urn:schemas-upnp-org:service:WANIPConnection:1") )
- "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
- datas->state ++;
- */
- if(0==strcmp(datas->tmp.servicetype,
- "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) {
- memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
- } else if(0==strcmp(datas->tmp.servicetype,
- "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1")) {
- memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
- } else if(0==strcmp(datas->tmp.servicetype,
- "urn:schemas-upnp-org:service:WANIPConnection:1")
- || 0==strcmp(datas->tmp.servicetype,
- "urn:schemas-upnp-org:service:WANPPPConnection:1") ) {
- if(datas->first.servicetype[0] == '\0') {
- memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
- } else {
- memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
- }
- }
- }
-}
-
-/* Data handler :
- * copy data depending on the current element name and state */
-void IGDdata(void * d, const char * data, int l)
-{
- struct IGDdatas * datas = (struct IGDdatas *)d;
- char * dstmember = 0;
- /*printf("%2d %s : %.*s\n",
- datas->level, datas->cureltname, l, data); */
- if( !strcmp(datas->cureltname, "URLBase") )
- dstmember = datas->urlbase;
- else if( !strcmp(datas->cureltname, "presentationURL") )
- dstmember = datas->presentationurl;
- else if( !strcmp(datas->cureltname, "serviceType") )
- dstmember = datas->tmp.servicetype;
- else if( !strcmp(datas->cureltname, "controlURL") )
- dstmember = datas->tmp.controlurl;
- else if( !strcmp(datas->cureltname, "eventSubURL") )
- dstmember = datas->tmp.eventsuburl;
- else if( !strcmp(datas->cureltname, "SCPDURL") )
- dstmember = datas->tmp.scpdurl;
-/* else if( !strcmp(datas->cureltname, "deviceType") )
- dstmember = datas->devicetype_tmp;*/
- if(dstmember)
- {
- if(l>=MINIUPNPC_URL_MAXSIZE)
- l = MINIUPNPC_URL_MAXSIZE-1;
- memcpy(dstmember, data, l);
- dstmember[l] = '\0';
- }
-}
-
-void printIGD(struct IGDdatas * d)
-{
- printf("urlbase = '%s'\n", d->urlbase);
- printf("WAN Device (Common interface config) :\n");
- /*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
- printf(" serviceType = '%s'\n", d->CIF.servicetype);
- printf(" controlURL = '%s'\n", d->CIF.controlurl);
- printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
- printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
- printf("primary WAN Connection Device (IP or PPP Connection):\n");
- /*printf(" deviceType = '%s'\n", d->first.devicetype);*/
- printf(" servicetype = '%s'\n", d->first.servicetype);
- printf(" controlURL = '%s'\n", d->first.controlurl);
- printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
- printf(" SCPDURL = '%s'\n", d->first.scpdurl);
- printf("secondary WAN Connection Device (IP or PPP Connection):\n");
- /*printf(" deviceType = '%s'\n", d->second.devicetype);*/
- printf(" servicetype = '%s'\n", d->second.servicetype);
- printf(" controlURL = '%s'\n", d->second.controlurl);
- printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
- printf(" SCPDURL = '%s'\n", d->second.scpdurl);
- printf("WAN IPv6 Firewall Control :\n");
- /*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
- printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
- printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
- printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
- printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
-}
-
-
+++ /dev/null
-/* $Id: igd_desc_parse.h,v 1.11 2012/10/16 16:49:02 nanard Exp $ */
-/* Project : miniupnp
- * http://miniupnp.free.fr/
- * Author : Thomas Bernard
- * Copyright (c) 2005-2010 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-#ifndef IGD_DESC_PARSE_H_INCLUDED
-#define IGD_DESC_PARSE_H_INCLUDED
-
-/* Structure to store the result of the parsing of UPnP
- * descriptions of Internet Gateway Devices */
-#define MINIUPNPC_URL_MAXSIZE (128)
-struct IGDdatas_service {
- char controlurl[MINIUPNPC_URL_MAXSIZE];
- char eventsuburl[MINIUPNPC_URL_MAXSIZE];
- char scpdurl[MINIUPNPC_URL_MAXSIZE];
- char servicetype[MINIUPNPC_URL_MAXSIZE];
- /*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
-};
-
-struct IGDdatas {
- char cureltname[MINIUPNPC_URL_MAXSIZE];
- char urlbase[MINIUPNPC_URL_MAXSIZE];
- char presentationurl[MINIUPNPC_URL_MAXSIZE];
- int level;
- /*int state;*/
- /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
- struct IGDdatas_service CIF;
- /* "urn:schemas-upnp-org:service:WANIPConnection:1"
- * "urn:schemas-upnp-org:service:WANPPPConnection:1" */
- struct IGDdatas_service first;
- /* if both WANIPConnection and WANPPPConnection are present */
- struct IGDdatas_service second;
- /* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
- struct IGDdatas_service IPv6FC;
- /* tmp */
- struct IGDdatas_service tmp;
-};
-
-void IGDstartelt(void *, const char *, int);
-void IGDendelt(void *, const char *, int);
-void IGDdata(void *, const char *, int);
-void printIGD(struct IGDdatas *);
-
-#endif
-
+++ /dev/null
-/* $Id: minisoap.c,v 1.22 2012/01/21 13:30:31 nanard Exp $ */
-/* Project : miniupnp
- * Author : Thomas Bernard
- * Copyright (c) 2005-2012 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- *
- * Minimal SOAP implementation for UPnP protocol.
- */
-#include <stdio.h>
-#include <string.h>
-#ifdef _WIN32
-#include <io.h>
-#include <winsock2.h>
-#define snprintf _snprintf
-#else
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#endif
-#include "minisoap.h"
-#include "miniupnpcstrings.h"
-
-/* only for malloc */
-#include <stdlib.h>
-
-#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
-#else
-#define PRINT_SOCKET_ERROR(x) perror(x)
-#endif
-
-/* httpWrite sends the headers and the body to the socket
- * and returns the number of bytes sent */
-static int
-httpWrite(int fd, const char * body, int bodysize,
- const char * headers, int headerssize)
-{
- int n = 0;
- /*n = write(fd, headers, headerssize);*/
- /*if(bodysize>0)
- n += write(fd, body, bodysize);*/
- /* Note : my old linksys router only took into account
- * soap request that are sent into only one packet */
- char * p;
- /* TODO: AVOID MALLOC */
- p = malloc(headerssize+bodysize);
- if(!p)
- return 0;
- memcpy(p, headers, headerssize);
- memcpy(p+headerssize, body, bodysize);
- /*n = write(fd, p, headerssize+bodysize);*/
- n = send(fd, p, headerssize+bodysize, 0);
- if(n<0) {
- PRINT_SOCKET_ERROR("send");
- }
- /* disable send on the socket */
- /* draytek routers dont seems to like that... */
-#if 0
-#ifdef _WIN32
- if(shutdown(fd, SD_SEND)<0) {
-#else
- if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/
-#endif
- PRINT_SOCKET_ERROR("shutdown");
- }
-#endif
- free(p);
- return n;
-}
-
-/* self explanatory */
-int soapPostSubmit(int fd,
- const char * url,
- const char * host,
- unsigned short port,
- const char * action,
- const char * body,
- const char * httpversion)
-{
- int bodysize;
- char headerbuf[512];
- int headerssize;
- char portstr[8];
- bodysize = (int)strlen(body);
- /* We are not using keep-alive HTTP connections.
- * HTTP/1.1 needs the header Connection: close to do that.
- * This is the default with HTTP/1.0
- * Using HTTP/1.1 means we need to support chunked transfer-encoding :
- * When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
- * transfer encoding. */
- /* Connection: Close is normally there only in HTTP/1.1 but who knows */
- portstr[0] = '\0';
- if(port != 80)
- snprintf(portstr, sizeof(portstr), ":%hu", port);
- headerssize = snprintf(headerbuf, sizeof(headerbuf),
- "POST %s HTTP/%s\r\n"
- "Host: %s%s\r\n"
- "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
- "Content-Length: %d\r\n"
- "Content-Type: text/xml\r\n"
- "SOAPAction: \"%s\"\r\n"
- "Connection: Close\r\n"
- "Cache-Control: no-cache\r\n" /* ??? */
- "Pragma: no-cache\r\n"
- "\r\n",
- url, httpversion, host, portstr, bodysize, action);
-#ifdef DEBUG
- /*printf("SOAP request : headersize=%d bodysize=%d\n",
- headerssize, bodysize);
- */
- printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n",
- url, httpversion, host, portstr);
- printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize);
- printf("Headers :\n%s", headerbuf);
- printf("Body :\n%s\n", body);
-#endif
- return httpWrite(fd, body, bodysize, headerbuf, headerssize);
-}
-
-
+++ /dev/null
-/* $Id: minisoap.h,v 1.5 2012/09/27 15:42:10 nanard Exp $ */
-/* Project : miniupnp
- * Author : Thomas Bernard
- * Copyright (c) 2005 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution. */
-#ifndef MINISOAP_H_INCLUDED
-#define MINISOAP_H_INCLUDED
-
-/*int httpWrite(int, const char *, int, const char *);*/
-int soapPostSubmit(int, const char *, const char *, unsigned short,
- const char *, const char *, const char *);
-
-#endif
-
+++ /dev/null
-/* $Id: minissdpc.c,v 1.16 2012/03/05 19:42:46 nanard Exp $ */
-/* Project : miniupnp
- * Web : http://miniupnp.free.fr/
- * Author : Thomas BERNARD
- * copyright (c) 2005-2012 Thomas Bernard
- * This software is subjet to the conditions detailed in the
- * provided LICENCE file. */
-/*#include <syslog.h>*/
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
-#ifdef _WIN32
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <io.h>
-#include <winsock.h>
-#include <stdint.h>
-#endif
-#if defined(__amigaos__) || defined(__amigaos4__)
-#include <sys/socket.h>
-#endif
-#if defined(__amigaos__)
-#define uint16_t unsigned short
-#endif
-/* Hack */
-#define UNIX_PATH_LEN 108
-struct sockaddr_un {
- uint16_t sun_family;
- char sun_path[UNIX_PATH_LEN];
-};
-#else
-#include <sys/socket.h>
-#include <sys/un.h>
-#endif
-
-#include "minissdpc.h"
-#include "miniupnpc.h"
-
-#include "codelength.h"
-
-struct UPNPDev *
-getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath)
-{
- struct UPNPDev * tmp;
- struct UPNPDev * devlist = NULL;
- unsigned char buffer[2048];
- ssize_t n;
- unsigned char * p;
- unsigned char * url;
- unsigned int i;
- unsigned int urlsize, stsize, usnsize, l;
- int s;
- struct sockaddr_un addr;
-
- s = socket(AF_UNIX, SOCK_STREAM, 0);
- if(s < 0)
- {
- /*syslog(LOG_ERR, "socket(unix): %m");*/
- perror("socket(unix)");
- return NULL;
- }
- addr.sun_family = AF_UNIX;
- strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
- /* TODO : check if we need to handle the EINTR */
- if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
- {
- /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
- close(s);
- return NULL;
- }
- stsize = strlen(devtype);
- buffer[0] = 1; /* request type 1 : request devices/services by type */
- p = buffer + 1;
- l = stsize; CODELENGTH(l, p);
- if(p + stsize > buffer + sizeof(buffer))
- {
- /* devtype is too long ! */
- close(s);
- return NULL;
- }
- memcpy(p, devtype, stsize);
- p += stsize;
- if(write(s, buffer, p - buffer) < 0)
- {
- /*syslog(LOG_ERR, "write(): %m");*/
- perror("minissdpc.c: write()");
- close(s);
- return NULL;
- }
- n = read(s, buffer, sizeof(buffer));
- if(n<=0)
- {
- perror("minissdpc.c: read()");
- close(s);
- return NULL;
- }
- p = buffer + 1;
- for(i = 0; i < buffer[0]; i++)
- {
- if(p+2>=buffer+sizeof(buffer))
- break;
- DECODELENGTH(urlsize, p);
- if(p+urlsize+2>=buffer+sizeof(buffer))
- break;
- url = p;
- p += urlsize;
- DECODELENGTH(stsize, p);
- if(p+stsize+2>=buffer+sizeof(buffer))
- break;
- tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
- tmp->pNext = devlist;
- tmp->descURL = tmp->buffer;
- tmp->st = tmp->buffer + 1 + urlsize;
- memcpy(tmp->buffer, url, urlsize);
- tmp->buffer[urlsize] = '\0';
- memcpy(tmp->buffer + urlsize + 1, p, stsize);
- p += stsize;
- tmp->buffer[urlsize+1+stsize] = '\0';
- devlist = tmp;
- /* added for compatibility with recent versions of MiniSSDPd
- * >= 2007/12/19 */
- DECODELENGTH(usnsize, p);
- p += usnsize;
- if(p>buffer + sizeof(buffer))
- break;
- }
- close(s);
- return devlist;
-}
-
+++ /dev/null
-/* $Id: minissdpc.h,v 1.2 2012/09/27 15:42:10 nanard Exp $ */
-/* Project: miniupnp
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * Author: Thomas Bernard
- * Copyright (c) 2005-2007 Thomas Bernard
- * This software is subjects to the conditions detailed
- * in the LICENCE file provided within this distribution */
-#ifndef MINISSDPC_H_INCLUDED
-#define MINISSDPC_H_INCLUDED
-
-struct UPNPDev *
-getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath);
-
-#endif
-
+++ /dev/null
-/* $Id: miniupnpc.c,v 1.117 2014/01/31 14:19:13 nanard Exp $ */
-/* Project : miniupnp
- * Web : http://miniupnp.free.fr/
- * Author : Thomas BERNARD
- * copyright (c) 2005-2014 Thomas Bernard
- * This software is subjet to the conditions detailed in the
- * provided LICENSE file. */
-#define __EXTENSIONS__ 1
-#if !defined(__APPLE__) && !defined(__sun)
-#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
-#ifndef __cplusplus
-#define _XOPEN_SOURCE 600
-#endif
-#endif
-#ifndef __BSD_VISIBLE
-#define __BSD_VISIBLE 1
-#endif
-#endif
-
-#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun)
-#define HAS_IP_MREQN
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#ifdef _WIN32
-/* Win32 Specific includes and defines */
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <io.h>
-#include <iphlpapi.h>
-#define snprintf _snprintf
-#define strdup _strdup
-#ifndef strncasecmp
-#if defined(_MSC_VER) && (_MSC_VER >= 1400)
-#define strncasecmp _memicmp
-#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
-#define strncasecmp memicmp
-#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
-#endif /* #ifndef strncasecmp */
-#define MAXHOSTNAMELEN 64
-#else /* #ifdef _WIN32 */
-/* Standard POSIX includes */
-#include <unistd.h>
-#if defined(__amigaos__) && !defined(__amigaos4__)
-/* Amiga OS 3 specific stuff */
-#define socklen_t int
-#else
-#include <sys/select.h>
-#endif
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/param.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <net/if.h>
-#if !defined(__amigaos__) && !defined(__amigaos4__)
-#include <poll.h>
-#endif
-#include <strings.h>
-#include <errno.h>
-#define closesocket close
-#endif /* #else _WIN32 */
-#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
-#include <sys/time.h>
-#endif
-#if defined(__amigaos__) || defined(__amigaos4__)
-/* Amiga OS specific stuff */
-#define TIMEVAL struct timeval
-#endif
-
-
-#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
-/* Several versions of glibc don't define this structure, define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
-struct ip_mreqn
-{
- struct in_addr imr_multiaddr; /* IP multicast address of group */
- struct in_addr imr_address; /* local IP address of interface */
- int imr_ifindex; /* Interface index */
-};
-#endif
-
-#include "miniupnpc.h"
-#include "minissdpc.h"
-#include "miniwget.h"
-#include "minisoap.h"
-#include "minixml.h"
-#include "upnpcommands.h"
-#include "connecthostport.h"
-#include "receivedata.h"
-
-#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
-#else
-#define PRINT_SOCKET_ERROR(x) perror(x)
-#endif
-
-#ifndef MAXHOSTNAMELEN
-#define MAXHOSTNAMELEN 64
-#endif
-
-#define SOAPPREFIX "s"
-#define SERVICEPREFIX "u"
-#define SERVICEPREFIX2 'u'
-
-/* root description parsing */
-LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
-{
- struct xmlparser parser;
- /* xmlparser object */
- parser.xmlstart = buffer;
- parser.xmlsize = bufsize;
- parser.data = data;
- parser.starteltfunc = IGDstartelt;
- parser.endeltfunc = IGDendelt;
- parser.datafunc = IGDdata;
- parser.attfunc = 0;
- parsexml(&parser);
-#ifdef DEBUG
- printIGD(data);
-#endif
-}
-
-/* simpleUPnPcommand2 :
- * not so simple !
- * return values :
- * pointer - OK
- * NULL - error */
-static char * simpleUPnPcommand2(int s, const char * url, const char * service,
- const char * action, struct UPNParg * args,
- int * bufsize, const char * httpversion)
-{
- char hostname[MAXHOSTNAMELEN+1];
- unsigned short port = 0;
- char * path;
- char soapact[128];
- char soapbody[2048];
- char * buf;
- int n;
-
- *bufsize = 0;
- snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
- if(args==NULL)
- {
- /*soapbodylen = */snprintf(soapbody, sizeof(soapbody),
- "<?xml version=\"1.0\"?>\r\n"
- "<" SOAPPREFIX ":Envelope "
- "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
- SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
- "<" SOAPPREFIX ":Body>"
- "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
- "</" SERVICEPREFIX ":%s>"
- "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
- "\r\n", action, service, action);
- }
- else
- {
- char * p;
- const char * pe, * pv;
- int soapbodylen;
- soapbodylen = snprintf(soapbody, sizeof(soapbody),
- "<?xml version=\"1.0\"?>\r\n"
- "<" SOAPPREFIX ":Envelope "
- "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
- SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
- "<" SOAPPREFIX ":Body>"
- "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
- action, service);
- p = soapbody + soapbodylen;
- while(args->elt)
- {
- /* check that we are never overflowing the string... */
- if(soapbody + sizeof(soapbody) <= p + 100)
- {
- /* we keep a margin of at least 100 bytes */
- return NULL;
- }
- *(p++) = '<';
- pe = args->elt;
- while(*pe)
- *(p++) = *(pe++);
- *(p++) = '>';
- if((pv = args->val))
- {
- while(*pv)
- *(p++) = *(pv++);
- }
- *(p++) = '<';
- *(p++) = '/';
- pe = args->elt;
- while(*pe)
- *(p++) = *(pe++);
- *(p++) = '>';
- args++;
- }
- *(p++) = '<';
- *(p++) = '/';
- *(p++) = SERVICEPREFIX2;
- *(p++) = ':';
- pe = action;
- while(*pe)
- *(p++) = *(pe++);
- strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
- soapbody + sizeof(soapbody) - p);
- }
- if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
- if(s < 0) {
- s = connecthostport(hostname, port, 0);
- if(s < 0) {
- /* failed to connect */
- return NULL;
- }
- }
-
- n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
- if(n<=0) {
-#ifdef DEBUG
- printf("Error sending SOAP request\n");
-#endif
- closesocket(s);
- return NULL;
- }
-
- buf = getHTTPResponse(s, bufsize);
-#ifdef DEBUG
- if(*bufsize > 0 && buf)
- {
- printf("SOAP Response :\n%.*s\n", *bufsize, buf);
- }
-#endif
- closesocket(s);
- return buf;
-}
-
-/* simpleUPnPcommand :
- * not so simple !
- * return values :
- * pointer - OK
- * NULL - error */
-char * simpleUPnPcommand(int s, const char * url, const char * service,
- const char * action, struct UPNParg * args,
- int * bufsize)
-{
- char * buf;
-
-#if 1
- buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
-#else
- buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0");
- if (!buf || *bufsize == 0)
- {
-#if DEBUG
- printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
-#endif
- buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
- }
-#endif
- return buf;
-}
-
-/* parseMSEARCHReply()
- * the last 4 arguments are filled during the parsing :
- * - location/locationsize : "location:" field of the SSDP reply packet
- * - st/stsize : "st:" field of the SSDP reply packet.
- * The strings are NOT null terminated */
-static void
-parseMSEARCHReply(const char * reply, int size,
- const char * * location, int * locationsize,
- const char * * st, int * stsize)
-{
- int a, b, i;
- i = 0;
- a = i; /* start of the line */
- b = 0; /* end of the "header" (position of the colon) */
- while(i<size)
- {
- switch(reply[i])
- {
- case ':':
- if(b==0)
- {
- b = i; /* end of the "header" */
- /*for(j=a; j<b; j++)
- {
- putchar(reply[j]);
- }
- */
- }
- break;
- case '\x0a':
- case '\x0d':
- if(b!=0)
- {
- /*for(j=b+1; j<i; j++)
- {
- putchar(reply[j]);
- }
- putchar('\n');*/
- /* skip the colon and white spaces */
- do { b++; } while(reply[b]==' ');
- if(0==strncasecmp(reply+a, "location", 8))
- {
- *location = reply+b;
- *locationsize = i-b;
- }
- else if(0==strncasecmp(reply+a, "st", 2))
- {
- *st = reply+b;
- *stsize = i-b;
- }
- b = 0;
- }
- a = i+1;
- break;
- default:
- break;
- }
- i++;
- }
-}
-
-/* port upnp discover : SSDP protocol */
-#define PORT 1900
-#define XSTR(s) STR(s)
-#define STR(s) #s
-#define UPNP_MCAST_ADDR "239.255.255.250"
-/* for IPv6 */
-#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
-#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
-
-/* upnpDiscover() :
- * return a chained list of all devices found or NULL if
- * no devices was found.
- * It is up to the caller to free the chained list
- * delay is in millisecond (poll) */
-LIBSPEC struct UPNPDev *
-upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock, int sameport,
- int ipv6,
- int * error)
-{
- struct UPNPDev * tmp;
- struct UPNPDev * devlist = 0;
- unsigned int scope_id = 0;
- int opt = 1;
- static const char MSearchMsgFmt[] =
- "M-SEARCH * HTTP/1.1\r\n"
- "HOST: %s:" XSTR(PORT) "\r\n"
- "ST: %s\r\n"
- "MAN: \"ssdp:discover\"\r\n"
- "MX: %u\r\n"
- "\r\n";
- static const char * const deviceList[] = {
-#if 0
- "urn:schemas-upnp-org:device:InternetGatewayDevice:2",
- "urn:schemas-upnp-org:service:WANIPConnection:2",
-#endif
- "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
- "urn:schemas-upnp-org:service:WANIPConnection:1",
- "urn:schemas-upnp-org:service:WANPPPConnection:1",
- "upnp:rootdevice",
- 0
- };
- int deviceIndex = 0;
- char bufr[1536]; /* reception and emission buffer */
- int sudp;
- int n;
- struct sockaddr_storage sockudp_r;
- unsigned int mx;
-#ifdef NO_GETADDRINFO
- struct sockaddr_storage sockudp_w;
-#else
- int rv;
- struct addrinfo hints, *servinfo, *p;
-#endif
-#ifdef _WIN32
- MIB_IPFORWARDROW ip_forward;
-#endif
- int linklocal = 1;
-
- if(error)
- *error = UPNPDISCOVER_UNKNOWN_ERROR;
-#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
- /* first try to get infos from minissdpd ! */
- if(!minissdpdsock)
- minissdpdsock = "/var/run/minissdpd.sock";
- while(!devlist && deviceList[deviceIndex]) {
- devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex],
- minissdpdsock);
- /* We return what we have found if it was not only a rootdevice */
- if(devlist && !strstr(deviceList[deviceIndex], "rootdevice")) {
- if(error)
- *error = UPNPDISCOVER_SUCCESS;
- return devlist;
- }
- deviceIndex++;
- }
- deviceIndex = 0;
-#endif
- /* fallback to direct discovery */
-#ifdef _WIN32
- sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
-#else
- sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
-#endif
- if(sudp < 0)
- {
- if(error)
- *error = UPNPDISCOVER_SOCKET_ERROR;
- PRINT_SOCKET_ERROR("socket");
- return NULL;
- }
- /* reception */
- memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
- if(ipv6) {
- struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
- p->sin6_family = AF_INET6;
- if(sameport)
- p->sin6_port = htons(PORT);
- p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
- } else {
- struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
- p->sin_family = AF_INET;
- if(sameport)
- p->sin_port = htons(PORT);
- p->sin_addr.s_addr = INADDR_ANY;
- }
-#ifdef _WIN32
-/* This code could help us to use the right Network interface for
- * SSDP multicast traffic */
-/* Get IP associated with the index given in the ip_forward struct
- * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
- if(!ipv6
- && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
- DWORD dwRetVal = 0;
- PMIB_IPADDRTABLE pIPAddrTable;
- DWORD dwSize = 0;
-#ifdef DEBUG
- IN_ADDR IPAddr;
-#endif
- int i;
-#ifdef DEBUG
- printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
-#endif
- pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
- if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
- free(pIPAddrTable);
- pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
- }
- if(pIPAddrTable) {
- dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
-#ifdef DEBUG
- printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
-#endif
- for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
-#ifdef DEBUG
- printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
- IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
- printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
- IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
- printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
- IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
- printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
- printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
- printf("\tType and State[%d]:", i);
- printf("\n");
-#endif
- if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
- /* Set the address of this interface to be used */
- struct in_addr mc_if;
- memset(&mc_if, 0, sizeof(mc_if));
- mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
- if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
- PRINT_SOCKET_ERROR("setsockopt");
- }
- ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
-#ifndef DEBUG
- break;
-#endif
- }
- }
- free(pIPAddrTable);
- pIPAddrTable = NULL;
- }
- }
-#endif
-
-#ifdef _WIN32
- if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
-#else
- if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
-#endif
- {
- if(error)
- *error = UPNPDISCOVER_SOCKET_ERROR;
- PRINT_SOCKET_ERROR("setsockopt");
- return NULL;
- }
-
- if(multicastif)
- {
- if(ipv6) {
-#if !defined(_WIN32)
- /* according to MSDN, if_nametoindex() is supported since
- * MS Windows Vista and MS Windows Server 2008.
- * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
- unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
- if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(&ifindex)) < 0)
- {
- PRINT_SOCKET_ERROR("setsockopt");
- }
-#else
-#ifdef DEBUG
- printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
-#endif
-#endif
- } else {
- struct in_addr mc_if;
- mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
- if(mc_if.s_addr != INADDR_NONE)
- {
- ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
- if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
- {
- PRINT_SOCKET_ERROR("setsockopt");
- }
- } else {
-#ifdef HAS_IP_MREQN
- /* was not an ip address, try with an interface name */
- struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
- memset(&reqn, 0, sizeof(struct ip_mreqn));
- reqn.imr_ifindex = if_nametoindex(multicastif);
- if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
- {
- PRINT_SOCKET_ERROR("setsockopt");
- }
-#else
-#ifdef DEBUG
- printf("Setting of multicast interface not supported with interface name.\n");
-#endif
-#endif
- }
- }
- }
-
- /* Before sending the packed, we first "bind" in order to be able
- * to receive the response */
- if (bind(sudp, (const struct sockaddr *)&sockudp_r,
- ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
- {
- if(error)
- *error = UPNPDISCOVER_SOCKET_ERROR;
- PRINT_SOCKET_ERROR("bind");
- closesocket(sudp);
- return NULL;
- }
-
- if(error)
- *error = UPNPDISCOVER_SUCCESS;
- /* Calculating maximum response time in seconds */
- mx = ((unsigned int)delay) / 1000u;
- if(mx == 0) {
- mx = 1;
- delay = 1000;
- }
- /* receiving SSDP response packet */
- for(n = 0; deviceList[deviceIndex]; deviceIndex++)
- {
- if(n == 0)
- {
- /* sending the SSDP M-SEARCH packet */
- n = snprintf(bufr, sizeof(bufr),
- MSearchMsgFmt,
- ipv6 ?
- (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
- : UPNP_MCAST_ADDR,
- deviceList[deviceIndex], mx);
-#ifdef DEBUG
- printf("Sending %s", bufr);
-#endif
-#ifdef NO_GETADDRINFO
- /* the following code is not using getaddrinfo */
- /* emission */
- memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
- if(ipv6) {
- struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
- p->sin6_family = AF_INET6;
- p->sin6_port = htons(PORT);
- inet_pton(AF_INET6,
- linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
- &(p->sin6_addr));
- } else {
- struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
- p->sin_family = AF_INET;
- p->sin_port = htons(PORT);
- p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
- }
- n = sendto(sudp, bufr, n, 0,
- &sockudp_w,
- ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
- if (n < 0) {
- if(error)
- *error = UPNPDISCOVER_SOCKET_ERROR;
- PRINT_SOCKET_ERROR("sendto");
- break;
- }
-#else /* #ifdef NO_GETADDRINFO */
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
- hints.ai_socktype = SOCK_DGRAM;
- /*hints.ai_flags = */
- if ((rv = getaddrinfo(ipv6
- ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
- : UPNP_MCAST_ADDR,
- XSTR(PORT), &hints, &servinfo)) != 0) {
- if(error)
- *error = UPNPDISCOVER_SOCKET_ERROR;
-#ifdef _WIN32
- fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
-#else
- fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
-#endif
- break;
- }
- for(p = servinfo; p; p = p->ai_next) {
- n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
- if (n < 0) {
-#ifdef DEBUG
- char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
- if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
- sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
- fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
- }
-#endif
- PRINT_SOCKET_ERROR("sendto");
- continue;
- }
- }
- freeaddrinfo(servinfo);
- if(n < 0) {
- if(error)
- *error = UPNPDISCOVER_SOCKET_ERROR;
- break;
- }
-#endif /* #ifdef NO_GETADDRINFO */
- }
- /* Waiting for SSDP REPLY packet to M-SEARCH */
- n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
- if (n < 0) {
- /* error */
- if(error)
- *error = UPNPDISCOVER_SOCKET_ERROR;
- break;
- } else if (n == 0) {
- /* no data or Time Out */
- if (devlist) {
- /* no more device type to look for... */
- if(error)
- *error = UPNPDISCOVER_SUCCESS;
- break;
- }
- if(ipv6) {
- if(linklocal) {
- linklocal = 0;
- --deviceIndex;
- } else {
- linklocal = 1;
- }
- }
- } else {
- const char * descURL=NULL;
- int urlsize=0;
- const char * st=NULL;
- int stsize=0;
- /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */
- parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
- if(st&&descURL)
- {
-#ifdef DEBUG
- printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
- stsize, st, urlsize, descURL);
-#endif
- for(tmp=devlist; tmp; tmp = tmp->pNext) {
- if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
- tmp->descURL[urlsize] == '\0' &&
- memcmp(tmp->st, st, stsize) == 0 &&
- tmp->st[stsize] == '\0')
- break;
- }
- /* at the exit of the loop above, tmp is null if
- * no duplicate device was found */
- if(tmp)
- continue;
- tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
- if(!tmp) {
- /* memory allocation error */
- if(error)
- *error = UPNPDISCOVER_MEMORY_ERROR;
- break;
- }
- tmp->pNext = devlist;
- tmp->descURL = tmp->buffer;
- tmp->st = tmp->buffer + 1 + urlsize;
- memcpy(tmp->buffer, descURL, urlsize);
- tmp->buffer[urlsize] = '\0';
- memcpy(tmp->buffer + urlsize + 1, st, stsize);
- tmp->buffer[urlsize+1+stsize] = '\0';
- tmp->scope_id = scope_id;
- devlist = tmp;
- }
- }
- }
- closesocket(sudp);
- return devlist;
-}
-
-/* freeUPNPDevlist() should be used to
- * free the chained list returned by upnpDiscover() */
-LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist)
-{
- struct UPNPDev * next;
- while(devlist)
- {
- next = devlist->pNext;
- free(devlist);
- devlist = next;
- }
-}
-
-static void
-url_cpy_or_cat(char * dst, const char * src, int n)
-{
- if( (src[0] == 'h')
- &&(src[1] == 't')
- &&(src[2] == 't')
- &&(src[3] == 'p')
- &&(src[4] == ':')
- &&(src[5] == '/')
- &&(src[6] == '/'))
- {
- strncpy(dst, src, n);
- }
- else
- {
- int l = strlen(dst);
- if(src[0] != '/')
- dst[l++] = '/';
- if(l<=n)
- strncpy(dst + l, src, n - l);
- }
-}
-
-/* Prepare the Urls for usage...
- */
-LIBSPEC void
-GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
- const char * descURL, unsigned int scope_id)
-{
- char * p;
- int n1, n2, n3, n4;
-#ifdef IF_NAMESIZE
- char ifname[IF_NAMESIZE];
-#else
- char scope_str[8];
-#endif
-
- n1 = strlen(data->urlbase);
- if(n1==0)
- n1 = strlen(descURL);
- if(scope_id != 0) {
-#ifdef IF_NAMESIZE
- if(if_indextoname(scope_id, ifname)) {
- n1 += 3 + strlen(ifname); /* 3 == strlen(%25) */
- }
-#else
- /* under windows, scope is numerical */
- snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
-#endif
- }
- n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
- n2 = n1; n3 = n1; n4 = n1;
- n1 += strlen(data->first.scpdurl);
- n2 += strlen(data->first.controlurl);
- n3 += strlen(data->CIF.controlurl);
- n4 += strlen(data->IPv6FC.controlurl);
-
- /* allocate memory to store URLs */
- urls->ipcondescURL = (char *)malloc(n1);
- urls->controlURL = (char *)malloc(n2);
- urls->controlURL_CIF = (char *)malloc(n3);
- urls->controlURL_6FC = (char *)malloc(n4);
-
- /* strdup descURL */
- urls->rootdescURL = strdup(descURL);
-
- /* get description of WANIPConnection */
- if(data->urlbase[0] != '\0')
- strncpy(urls->ipcondescURL, data->urlbase, n1);
- else
- strncpy(urls->ipcondescURL, descURL, n1);
- p = strchr(urls->ipcondescURL+7, '/');
- if(p) p[0] = '\0';
- if(scope_id != 0) {
- if(0 == memcmp(urls->ipcondescURL, "http://[fe80:", 13)) {
- /* this is a linklocal IPv6 address */
- p = strchr(urls->ipcondescURL, ']');
- if(p) {
- /* insert %25<scope> into URL */
-#ifdef IF_NAMESIZE
- memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
- memcpy(p, "%25", 3);
- memcpy(p + 3, ifname, strlen(ifname));
-#else
- memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
- memcpy(p, "%25", 3);
- memcpy(p + 3, scope_str, strlen(scope_str));
-#endif
- }
- }
- }
- strncpy(urls->controlURL, urls->ipcondescURL, n2);
- strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
- strncpy(urls->controlURL_6FC, urls->ipcondescURL, n4);
-
- url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
-
- url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
-
- url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
-
- url_cpy_or_cat(urls->controlURL_6FC, data->IPv6FC.controlurl, n4);
-
-#ifdef DEBUG
- printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
- (unsigned)strlen(urls->ipcondescURL), n1);
- printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
- (unsigned)strlen(urls->controlURL), n2);
- printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
- (unsigned)strlen(urls->controlURL_CIF), n3);
- printf("urls->controlURL_6FC='%s' %u n4=%d\n", urls->controlURL_6FC,
- (unsigned)strlen(urls->controlURL_6FC), n4);
-#endif
-}
-
-LIBSPEC void
-FreeUPNPUrls(struct UPNPUrls * urls)
-{
- if(!urls)
- return;
- free(urls->controlURL);
- urls->controlURL = 0;
- free(urls->ipcondescURL);
- urls->ipcondescURL = 0;
- free(urls->controlURL_CIF);
- urls->controlURL_CIF = 0;
- free(urls->controlURL_6FC);
- urls->controlURL_6FC = 0;
- free(urls->rootdescURL);
- urls->rootdescURL = 0;
-}
-
-int
-UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
-{
- char status[64];
- unsigned int uptime;
- status[0] = '\0';
- UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
- status, &uptime, NULL);
- if(0 == strcmp("Connected", status))
- {
- return 1;
- }
- else
- return 0;
-}
-
-
-/* UPNP_GetValidIGD() :
- * return values :
- * -1 = Internal error
- * 0 = NO IGD found
- * 1 = A valid connected IGD has been found
- * 2 = A valid IGD has been found but it reported as
- * not connected
- * 3 = an UPnP device has been found but was not recognized as an IGD
- *
- * In any positive non zero return case, the urls and data structures
- * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
- * free allocated memory.
- */
-LIBSPEC int
-UPNP_GetValidIGD(struct UPNPDev * devlist,
- struct UPNPUrls * urls,
- struct IGDdatas * data,
- char * lanaddr, int lanaddrlen)
-{
- struct xml_desc {
- char * xml;
- int size;
- int is_igd;
- } * desc = NULL;
- struct UPNPDev * dev;
- int ndev = 0;
- int i;
- int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
- int n_igd = 0;
- char extIpAddr[16];
- if(!devlist)
- {
-#ifdef DEBUG
- printf("Empty devlist\n");
-#endif
- return 0;
- }
- /* counting total number of devices in the list */
- for(dev = devlist; dev; dev = dev->pNext)
- ndev++;
- if(ndev > 0)
- {
- desc = calloc(ndev, sizeof(struct xml_desc));
- if(!desc)
- return -1; /* memory allocation error */
- }
- /* Step 1 : downloading descriptions and testing type */
- for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
- {
- /* we should choose an internet gateway device.
- * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
- desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
- lanaddr, lanaddrlen,
- dev->scope_id);
-#ifdef DEBUG
- if(!desc[i].xml)
- {
- printf("error getting XML description %s\n", dev->descURL);
- }
-#endif
- if(desc[i].xml)
- {
- memset(data, 0, sizeof(struct IGDdatas));
- memset(urls, 0, sizeof(struct UPNPUrls));
- parserootdesc(desc[i].xml, desc[i].size, data);
- if(0==strcmp(data->CIF.servicetype,
- "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
- {
- desc[i].is_igd = 1;
- n_igd++;
- }
- }
- }
- /* iterate the list to find a device depending on state */
- for(state = 1; state <= 3; state++)
- {
- for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
- {
- if(desc[i].xml)
- {
- memset(data, 0, sizeof(struct IGDdatas));
- memset(urls, 0, sizeof(struct UPNPUrls));
- parserootdesc(desc[i].xml, desc[i].size, data);
- if(desc[i].is_igd || state >= 3 )
- {
- GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
-
- /* in state 2 and 3 we dont test if device is connected ! */
- if(state >= 2)
- goto free_and_return;
-#ifdef DEBUG
- printf("UPNPIGD_IsConnected(%s) = %d\n",
- urls->controlURL,
- UPNPIGD_IsConnected(urls, data));
-#endif
- /* checks that status is connected AND there is a external IP address assigned */
- if(UPNPIGD_IsConnected(urls, data)
- && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0))
- goto free_and_return;
- FreeUPNPUrls(urls);
- if(data->second.servicetype[0] != '\0') {
-#ifdef DEBUG
- printf("We tried %s, now we try %s !\n",
- data->first.servicetype, data->second.servicetype);
-#endif
- /* swaping WANPPPConnection and WANIPConnection ! */
- memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
- memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
- memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
- GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
-#ifdef DEBUG
- printf("UPNPIGD_IsConnected(%s) = %d\n",
- urls->controlURL,
- UPNPIGD_IsConnected(urls, data));
-#endif
- if(UPNPIGD_IsConnected(urls, data)
- && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0))
- goto free_and_return;
- FreeUPNPUrls(urls);
- }
- }
- memset(data, 0, sizeof(struct IGDdatas));
- }
- }
- }
- state = 0;
-free_and_return:
- if(desc) {
- for(i = 0; i < ndev; i++) {
- if(desc[i].xml) {
- free(desc[i].xml);
- }
- }
- free(desc);
- }
- return state;
-}
-
-/* UPNP_GetIGDFromUrl()
- * Used when skipping the discovery process.
- * return value :
- * 0 - Not ok
- * 1 - OK */
-int
-UPNP_GetIGDFromUrl(const char * rootdescurl,
- struct UPNPUrls * urls,
- struct IGDdatas * data,
- char * lanaddr, int lanaddrlen)
-{
- char * descXML;
- int descXMLsize = 0;
- descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
- lanaddr, lanaddrlen, 0);
- if(descXML) {
- memset(data, 0, sizeof(struct IGDdatas));
- memset(urls, 0, sizeof(struct UPNPUrls));
- parserootdesc(descXML, descXMLsize, data);
- free(descXML);
- descXML = NULL;
- GetUPNPUrls(urls, data, rootdescurl, 0);
- return 1;
- } else {
- return 0;
- }
-}
-
+++ /dev/null
-/* $Id: miniupnpc.h,v 1.35 2014/01/31 13:26:34 nanard Exp $ */
-/* Project: miniupnp
- * http://miniupnp.free.fr/
- * Author: Thomas Bernard
- * Copyright (c) 2005-2012 Thomas Bernard
- * This software is subjects to the conditions detailed
- * in the LICENCE file provided within this distribution */
-#ifndef MINIUPNPC_H_INCLUDED
-#define MINIUPNPC_H_INCLUDED
-
-#include "declspec.h"
-#include "igd_desc_parse.h"
-
-/* error codes : */
-#define UPNPDISCOVER_SUCCESS (0)
-#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
-#define UPNPDISCOVER_SOCKET_ERROR (-101)
-#define UPNPDISCOVER_MEMORY_ERROR (-102)
-
-/* versions : */
-#define MINIUPNPC_VERSION "1.9.20140401"
-#define MINIUPNPC_API_VERSION 10
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Structures definitions : */
-struct UPNParg { const char * elt; const char * val; };
-
-char *
-simpleUPnPcommand(int, const char *, const char *,
- const char *, struct UPNParg *,
- int *);
-
-struct UPNPDev {
- struct UPNPDev * pNext;
- char * descURL;
- char * st;
- unsigned int scope_id;
- char buffer[2];
-};
-
-/* upnpDiscover()
- * discover UPnP devices on the network.
- * The discovered devices are returned as a chained list.
- * It is up to the caller to free the list with freeUPNPDevlist().
- * delay (in millisecond) is the maximum time for waiting any device
- * response.
- * If available, device list will be obtained from MiniSSDPd.
- * Default path for minissdpd socket will be used if minissdpdsock argument
- * is NULL.
- * If multicastif is not NULL, it will be used instead of the default
- * multicast interface for sending SSDP discover packets.
- * If sameport is not null, SSDP packets will be sent from the source port
- * 1900 (same as destination port) otherwise system assign a source port. */
-LIBSPEC struct UPNPDev *
-upnpDiscover(int delay, const char * multicastif,
- const char * minissdpdsock, int sameport,
- int ipv6,
- int * error);
-/* freeUPNPDevlist()
- * free list returned by upnpDiscover() */
-LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist);
-
-/* parserootdesc() :
- * parse root XML description of a UPnP device and fill the IGDdatas
- * structure. */
-LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
-
-/* structure used to get fast access to urls
- * controlURL: controlURL of the WANIPConnection
- * ipcondescURL: url of the description of the WANIPConnection
- * controlURL_CIF: controlURL of the WANCommonInterfaceConfig
- * controlURL_6FC: controlURL of the WANIPv6FirewallControl
- */
-struct UPNPUrls {
- char * controlURL;
- char * ipcondescURL;
- char * controlURL_CIF;
- char * controlURL_6FC;
- char * rootdescURL;
-};
-
-/* UPNP_GetValidIGD() :
- * return values :
- * 0 = NO IGD found
- * 1 = A valid connected IGD has been found
- * 2 = A valid IGD has been found but it reported as
- * not connected
- * 3 = an UPnP device has been found but was not recognized as an IGD
- *
- * In any non zero return case, the urls and data structures
- * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
- * free allocated memory.
- */
-LIBSPEC int
-UPNP_GetValidIGD(struct UPNPDev * devlist,
- struct UPNPUrls * urls,
- struct IGDdatas * data,
- char * lanaddr, int lanaddrlen);
-
-/* UPNP_GetIGDFromUrl()
- * Used when skipping the discovery process.
- * return value :
- * 0 - Not ok
- * 1 - OK */
-LIBSPEC int
-UPNP_GetIGDFromUrl(const char * rootdescurl,
- struct UPNPUrls * urls,
- struct IGDdatas * data,
- char * lanaddr, int lanaddrlen);
-
-LIBSPEC void
-GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *,
- const char *, unsigned int);
-
-LIBSPEC void
-FreeUPNPUrls(struct UPNPUrls *);
-
-/* return 0 or 1 */
-LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
+++ /dev/null
-/* $Id: miniupnpcstrings.h.in,v 1.5 2012/10/16 16:48:26 nanard Exp $ */
-/* Project: miniupnp
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * Author: Thomas Bernard
- * Copyright (c) 2005-2011 Thomas Bernard
- * This software is subjects to the conditions detailed
- * in the LICENCE file provided within this distribution */
-#ifndef MINIUPNPCSTRINGS_H_INCLUDED
-#define MINIUPNPCSTRINGS_H_INCLUDED
-
-#define OS_STRING "OS/version"
-#define MINIUPNPC_VERSION_STRING "version"
-
-#endif
-
+++ /dev/null
-/* $Id: miniupnpctypes.h,v 1.2 2012/09/27 15:42:10 nanard Exp $ */
-/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org
- * Author : Thomas Bernard
- * Copyright (c) 2011 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided within this distribution */
-#ifndef MINIUPNPCTYPES_H_INCLUDED
-#define MINIUPNPCTYPES_H_INCLUDED
-
-#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
-#define UNSIGNED_INTEGER unsigned long long
-#define STRTOUI strtoull
-#else
-#define UNSIGNED_INTEGER unsigned int
-#define STRTOUI strtoul
-#endif
-
-#endif
-
+++ /dev/null
-/* $Id: miniwget.c,v 1.61 2014/02/05 17:27:48 nanard Exp $ */
-/* Project : miniupnp
- * Website : http://miniupnp.free.fr/
- * Author : Thomas Bernard
- * Copyright (c) 2005-2014 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution. */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#ifdef _WIN32
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#include <io.h>
-#define MAXHOSTNAMELEN 64
-#define MIN(x,y) (((x)<(y))?(x):(y))
-#define snprintf _snprintf
-#define socklen_t int
-#ifndef strncasecmp
-#if defined(_MSC_VER) && (_MSC_VER >= 1400)
-#define strncasecmp _memicmp
-#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
-#define strncasecmp memicmp
-#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
-#endif /* #ifndef strncasecmp */
-#else /* #ifdef _WIN32 */
-#include <unistd.h>
-#include <sys/param.h>
-#if defined(__amigaos__) && !defined(__amigaos4__)
-#define socklen_t int
-#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
-#include <sys/select.h>
-#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-#include <netdb.h>
-#define closesocket close
-#endif /* #else _WIN32 */
-#if defined(__sun) || defined(sun)
-#define MIN(x,y) (((x)<(y))?(x):(y))
-#endif
-
-#include "miniupnpcstrings.h"
-#include "miniwget.h"
-#include "connecthostport.h"
-#include "receivedata.h"
-
-#ifndef MAXHOSTNAMELEN
-#define MAXHOSTNAMELEN 64
-#endif
-
-/*
- * Read a HTTP response from a socket.
- * Process Content-Length and Transfer-encoding headers.
- * return a pointer to the content buffer, which length is saved
- * to the length parameter.
- */
-void *
-getHTTPResponse(int s, int * size)
-{
- char buf[2048];
- int n;
- int endofheaders = 0;
- int chunked = 0;
- int content_length = -1;
- unsigned int chunksize = 0;
- unsigned int bytestocopy = 0;
- /* buffers : */
- char * header_buf;
- unsigned int header_buf_len = 2048;
- unsigned int header_buf_used = 0;
- char * content_buf;
- unsigned int content_buf_len = 2048;
- unsigned int content_buf_used = 0;
- char chunksize_buf[32];
- unsigned int chunksize_buf_index;
-
- header_buf = malloc(header_buf_len);
- content_buf = malloc(content_buf_len);
- chunksize_buf[0] = '\0';
- chunksize_buf_index = 0;
-
- while((n = receivedata(s, buf, 2048, 5000, NULL)) > 0)
- {
- if(endofheaders == 0)
- {
- int i;
- int linestart=0;
- int colon=0;
- int valuestart=0;
- if(header_buf_used + n > header_buf_len) {
- header_buf = realloc(header_buf, header_buf_used + n);
- header_buf_len = header_buf_used + n;
- }
- memcpy(header_buf + header_buf_used, buf, n);
- header_buf_used += n;
- /* search for CR LF CR LF (end of headers)
- * recognize also LF LF */
- i = 0;
- while(i < ((int)header_buf_used-1) && (endofheaders == 0)) {
- if(header_buf[i] == '\r') {
- i++;
- if(header_buf[i] == '\n') {
- i++;
- if(i < (int)header_buf_used && header_buf[i] == '\r') {
- i++;
- if(i < (int)header_buf_used && header_buf[i] == '\n') {
- endofheaders = i+1;
- }
- }
- }
- } else if(header_buf[i] == '\n') {
- i++;
- if(header_buf[i] == '\n') {
- endofheaders = i+1;
- }
- }
- i++;
- }
- if(endofheaders == 0)
- continue;
- /* parse header lines */
- for(i = 0; i < endofheaders - 1; i++) {
- if(colon <= linestart && header_buf[i]==':')
- {
- colon = i;
- while(i < (endofheaders-1)
- && (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
- i++;
- valuestart = i + 1;
- }
- /* detecting end of line */
- else if(header_buf[i]=='\r' || header_buf[i]=='\n')
- {
- if(colon > linestart && valuestart > colon)
- {
-#ifdef DEBUG
- printf("header='%.*s', value='%.*s'\n",
- colon-linestart, header_buf+linestart,
- i-valuestart, header_buf+valuestart);
-#endif
- if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
- {
- content_length = atoi(header_buf+valuestart);
-#ifdef DEBUG
- printf("Content-Length: %d\n", content_length);
-#endif
- }
- else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
- && 0==strncasecmp(header_buf+valuestart, "chunked", 7))
- {
-#ifdef DEBUG
- printf("chunked transfer-encoding!\n");
-#endif
- chunked = 1;
- }
- }
- while((i < (int)header_buf_used) && (header_buf[i]=='\r' || header_buf[i] == '\n'))
- i++;
- linestart = i;
- colon = linestart;
- valuestart = 0;
- }
- }
- /* copy the remaining of the received data back to buf */
- n = header_buf_used - endofheaders;
- memcpy(buf, header_buf + endofheaders, n);
- /* if(headers) */
- }
- if(endofheaders)
- {
- /* content */
- if(chunked)
- {
- int i = 0;
- while(i < n)
- {
- if(chunksize == 0)
- {
- /* reading chunk size */
- if(chunksize_buf_index == 0) {
- /* skipping any leading CR LF */
- if(i<n && buf[i] == '\r') i++;
- if(i<n && buf[i] == '\n') i++;
- }
- while(i<n && isxdigit(buf[i])
- && chunksize_buf_index < (sizeof(chunksize_buf)-1))
- {
- chunksize_buf[chunksize_buf_index++] = buf[i];
- chunksize_buf[chunksize_buf_index] = '\0';
- i++;
- }
- while(i<n && buf[i] != '\r' && buf[i] != '\n')
- i++; /* discarding chunk-extension */
- if(i<n && buf[i] == '\r') i++;
- if(i<n && buf[i] == '\n') {
- unsigned int j;
- for(j = 0; j < chunksize_buf_index; j++) {
- if(chunksize_buf[j] >= '0'
- && chunksize_buf[j] <= '9')
- chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
- else
- chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
- }
- chunksize_buf[0] = '\0';
- chunksize_buf_index = 0;
- i++;
- } else {
- /* not finished to get chunksize */
- continue;
- }
-#ifdef DEBUG
- printf("chunksize = %u (%x)\n", chunksize, chunksize);
-#endif
- if(chunksize == 0)
- {
-#ifdef DEBUG
- printf("end of HTTP content - %d %d\n", i, n);
- /*printf("'%.*s'\n", n-i, buf+i);*/
-#endif
- goto end_of_stream;
- }
- }
- bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i);
- if((content_buf_used + bytestocopy) > content_buf_len)
- {
- if(content_length >= (int)(content_buf_used + bytestocopy)) {
- content_buf_len = content_length;
- } else {
- content_buf_len = content_buf_used + bytestocopy;
- }
- content_buf = (char *)realloc((void *)content_buf,
- content_buf_len);
- }
- memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
- content_buf_used += bytestocopy;
- i += bytestocopy;
- chunksize -= bytestocopy;
- }
- }
- else
- {
- /* not chunked */
- if(content_length > 0
- && (int)(content_buf_used + n) > content_length) {
- /* skipping additional bytes */
- n = content_length - content_buf_used;
- }
- if(content_buf_used + n > content_buf_len)
- {
- if(content_length >= (int)(content_buf_used + n)) {
- content_buf_len = content_length;
- } else {
- content_buf_len = content_buf_used + n;
- }
- content_buf = (char *)realloc((void *)content_buf,
- content_buf_len);
- }
- memcpy(content_buf + content_buf_used, buf, n);
- content_buf_used += n;
- }
- }
- /* use the Content-Length header value if available */
- if(content_length > 0 && (int)content_buf_used >= content_length)
- {
-#ifdef DEBUG
- printf("End of HTTP content\n");
-#endif
- break;
- }
- }
-end_of_stream:
- free(header_buf); header_buf = NULL;
- *size = content_buf_used;
- if(content_buf_used == 0)
- {
- free(content_buf);
- content_buf = NULL;
- }
- return content_buf;
-}
-
-/* miniwget3() :
- * do all the work.
- * Return NULL if something failed. */
-static void *
-miniwget3(const char * host,
- unsigned short port, const char * path,
- int * size, char * addr_str, int addr_str_len,
- const char * httpversion, unsigned int scope_id)
-{
- char buf[2048];
- int s;
- int n;
- int len;
- int sent;
- void * content;
-
- *size = 0;
- s = connecthostport(host, port, scope_id);
- if(s < 0)
- return NULL;
-
- /* get address for caller ! */
- if(addr_str)
- {
- struct sockaddr_storage saddr;
- socklen_t saddrlen;
-
- saddrlen = sizeof(saddr);
- if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
- {
- perror("getsockname");
- }
- else
- {
-#if defined(__amigaos__) && !defined(__amigaos4__)
- /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
- * But his function make a string with the port : nn.nn.nn.nn:port */
-/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
- NULL, addr_str, (DWORD *)&addr_str_len))
- {
- printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
- }*/
- /* the following code is only compatible with ip v4 addresses */
- strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
-#else
-#if 0
- if(saddr.sa_family == AF_INET6) {
- inet_ntop(AF_INET6,
- &(((struct sockaddr_in6 *)&saddr)->sin6_addr),
- addr_str, addr_str_len);
- } else {
- inet_ntop(AF_INET,
- &(((struct sockaddr_in *)&saddr)->sin_addr),
- addr_str, addr_str_len);
- }
-#endif
- /* getnameinfo return ip v6 address with the scope identifier
- * such as : 2a01:e35:8b2b:7330::%4281128194 */
- n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
- addr_str, addr_str_len,
- NULL, 0,
- NI_NUMERICHOST | NI_NUMERICSERV);
- if(n != 0) {
-#ifdef _WIN32
- fprintf(stderr, "getnameinfo() failed : %d\n", n);
-#else
- fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
-#endif
- }
-#endif
- }
-#ifdef DEBUG
- printf("address miniwget : %s\n", addr_str);
-#endif
- }
-
- len = snprintf(buf, sizeof(buf),
- "GET %s HTTP/%s\r\n"
- "Host: %s:%d\r\n"
- "Connection: Close\r\n"
- "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
-
- "\r\n",
- path, httpversion, host, port);
- sent = 0;
- /* sending the HTTP request */
- while(sent < len)
- {
- n = send(s, buf+sent, len-sent, 0);
- if(n < 0)
- {
- perror("send");
- closesocket(s);
- return NULL;
- }
- else
- {
- sent += n;
- }
- }
- content = getHTTPResponse(s, size);
- closesocket(s);
- return content;
-}
-
-/* miniwget2() :
- * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
-static void *
-miniwget2(const char * host,
- unsigned short port, const char * path,
- int * size, char * addr_str, int addr_str_len,
- unsigned int scope_id)
-{
- char * respbuffer;
-
-#if 1
- respbuffer = miniwget3(host, port, path, size,
- addr_str, addr_str_len, "1.1", scope_id);
-#else
- respbuffer = miniwget3(host, port, path, size,
- addr_str, addr_str_len, "1.0", scope_id);
- if (*size == 0)
- {
-#ifdef DEBUG
- printf("Retrying with HTTP/1.1\n");
-#endif
- free(respbuffer);
- respbuffer = miniwget3(host, port, path, size,
- addr_str, addr_str_len, "1.1", scope_id);
- }
-#endif
- return respbuffer;
-}
-
-
-
-
-/* parseURL()
- * arguments :
- * url : source string not modified
- * hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
- * port : port (destination)
- * path : pointer to the path part of the URL
- *
- * Return values :
- * 0 - Failure
- * 1 - Success */
-int
-parseURL(const char * url,
- char * hostname, unsigned short * port,
- char * * path, unsigned int * scope_id)
-{
- char * p1, *p2, *p3;
- if(!url)
- return 0;
- p1 = strstr(url, "://");
- if(!p1)
- return 0;
- p1 += 3;
- if( (url[0]!='h') || (url[1]!='t')
- ||(url[2]!='t') || (url[3]!='p'))
- return 0;
- memset(hostname, 0, MAXHOSTNAMELEN + 1);
- if(*p1 == '[')
- {
- /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
- char * scope;
- scope = strchr(p1, '%');
- p2 = strchr(p1, ']');
- if(p2 && scope && scope < p2 && scope_id) {
- /* parse scope */
-#ifdef IF_NAMESIZE
- char tmp[IF_NAMESIZE];
- int l;
- scope++;
- /* "%25" is just '%' in URL encoding */
- if(scope[0] == '2' && scope[1] == '5')
- scope += 2; /* skip "25" */
- l = p2 - scope;
- if(l >= IF_NAMESIZE)
- l = IF_NAMESIZE - 1;
- memcpy(tmp, scope, l);
- tmp[l] = '\0';
- *scope_id = if_nametoindex(tmp);
- if(*scope_id == 0) {
- *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
- }
-#else
- /* under windows, scope is numerical */
- char tmp[8];
- int l;
- scope++;
- /* "%25" is just '%' in URL encoding */
- if(scope[0] == '2' && scope[1] == '5')
- scope += 2; /* skip "25" */
- l = p2 - scope;
- if(l >= sizeof(tmp))
- l = sizeof(tmp) - 1;
- memcpy(tmp, scope, l);
- tmp[l] = '\0';
- *scope_id = (unsigned int)strtoul(tmp, NULL, 10);
-#endif
- }
- p3 = strchr(p1, '/');
- if(p2 && p3)
- {
- p2++;
- strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
- if(*p2 == ':')
- {
- *port = 0;
- p2++;
- while( (*p2 >= '0') && (*p2 <= '9'))
- {
- *port *= 10;
- *port += (unsigned short)(*p2 - '0');
- p2++;
- }
- }
- else
- {
- *port = 80;
- }
- *path = p3;
- return 1;
- }
- }
- p2 = strchr(p1, ':');
- p3 = strchr(p1, '/');
- if(!p3)
- return 0;
- if(!p2 || (p2>p3))
- {
- strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
- *port = 80;
- }
- else
- {
- strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
- *port = 0;
- p2++;
- while( (*p2 >= '0') && (*p2 <= '9'))
- {
- *port *= 10;
- *port += (unsigned short)(*p2 - '0');
- p2++;
- }
- }
- *path = p3;
- return 1;
-}
-
-void *
-miniwget(const char * url, int * size, unsigned int scope_id)
-{
- unsigned short port;
- char * path;
- /* protocol://host:port/chemin */
- char hostname[MAXHOSTNAMELEN+1];
- *size = 0;
- if(!parseURL(url, hostname, &port, &path, &scope_id))
- return NULL;
-#ifdef DEBUG
- printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
- hostname, port, path, scope_id);
-#endif
- return miniwget2(hostname, port, path, size, 0, 0, scope_id);
-}
-
-void *
-miniwget_getaddr(const char * url, int * size,
- char * addr, int addrlen, unsigned int scope_id)
-{
- unsigned short port;
- char * path;
- /* protocol://host:port/path */
- char hostname[MAXHOSTNAMELEN+1];
- *size = 0;
- if(addr)
- addr[0] = '\0';
- if(!parseURL(url, hostname, &port, &path, &scope_id))
- return NULL;
-#ifdef DEBUG
- printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
- hostname, port, path, scope_id);
-#endif
- return miniwget2(hostname, port, path, size, addr, addrlen, scope_id);
-}
-
+++ /dev/null
-/* $Id: miniwget.h,v 1.8 2012/09/27 15:42:10 nanard Exp $ */
-/* Project : miniupnp
- * Author : Thomas Bernard
- * Copyright (c) 2005-2012 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-#ifndef MINIWGET_H_INCLUDED
-#define MINIWGET_H_INCLUDED
-
-#include "declspec.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-LIBSPEC void * getHTTPResponse(int s, int * size);
-
-LIBSPEC void * miniwget(const char *, int *, unsigned int);
-
-LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int);
-
-int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
+++ /dev/null
-/* $Id: minixml.c,v 1.11 2014/02/03 15:54:12 nanard Exp $ */
-/* minixml.c : the minimum size a xml parser can be ! */
-/* Project : miniupnp
- * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * Author : Thomas Bernard
-
-Copyright (c) 2005-2014, Thomas BERNARD
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-*/
-#include <string.h>
-#include "minixml.h"
-
-/* parseatt : used to parse the argument list
- * return 0 (false) in case of success and -1 (true) if the end
- * of the xmlbuffer is reached. */
-static int parseatt(struct xmlparser * p)
-{
- const char * attname;
- int attnamelen;
- const char * attvalue;
- int attvaluelen;
- while(p->xml < p->xmlend)
- {
- if(*p->xml=='/' || *p->xml=='>')
- return 0;
- if( !IS_WHITE_SPACE(*p->xml) )
- {
- char sep;
- attname = p->xml;
- attnamelen = 0;
- while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
- {
- attnamelen++; p->xml++;
- if(p->xml >= p->xmlend)
- return -1;
- }
- while(*(p->xml++) != '=')
- {
- if(p->xml >= p->xmlend)
- return -1;
- }
- while(IS_WHITE_SPACE(*p->xml))
- {
- p->xml++;
- if(p->xml >= p->xmlend)
- return -1;
- }
- sep = *p->xml;
- if(sep=='\'' || sep=='\"')
- {
- p->xml++;
- if(p->xml >= p->xmlend)
- return -1;
- attvalue = p->xml;
- attvaluelen = 0;
- while(*p->xml != sep)
- {
- attvaluelen++; p->xml++;
- if(p->xml >= p->xmlend)
- return -1;
- }
- }
- else
- {
- attvalue = p->xml;
- attvaluelen = 0;
- while( !IS_WHITE_SPACE(*p->xml)
- && *p->xml != '>' && *p->xml != '/')
- {
- attvaluelen++; p->xml++;
- if(p->xml >= p->xmlend)
- return -1;
- }
- }
- /*printf("%.*s='%.*s'\n",
- attnamelen, attname, attvaluelen, attvalue);*/
- if(p->attfunc)
- p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
- }
- p->xml++;
- }
- return -1;
-}
-
-/* parseelt parse the xml stream and
- * call the callback functions when needed... */
-static void parseelt(struct xmlparser * p)
-{
- int i;
- const char * elementname;
- while(p->xml < (p->xmlend - 1))
- {
- if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "<!--", 4)))
- {
- p->xml += 3;
- /* ignore comments */
- do
- {
- p->xml++;
- if ((p->xml + 3) >= p->xmlend)
- return;
- }
- while(memcmp(p->xml, "-->", 3) != 0);
- p->xml += 3;
- }
- else if((p->xml)[0]=='<' && (p->xml)[1]!='?')
- {
- i = 0; elementname = ++p->xml;
- while( !IS_WHITE_SPACE(*p->xml)
- && (*p->xml!='>') && (*p->xml!='/')
- )
- {
- i++; p->xml++;
- if (p->xml >= p->xmlend)
- return;
- /* to ignore namespace : */
- if(*p->xml==':')
- {
- i = 0;
- elementname = ++p->xml;
- }
- }
- if(i>0)
- {
- if(p->starteltfunc)
- p->starteltfunc(p->data, elementname, i);
- if(parseatt(p))
- return;
- if(*p->xml!='/')
- {
- const char * data;
- i = 0; data = ++p->xml;
- if (p->xml >= p->xmlend)
- return;
- while( IS_WHITE_SPACE(*p->xml) )
- {
- i++; p->xml++;
- if (p->xml >= p->xmlend)
- return;
- }
- if(memcmp(p->xml, "<![CDATA[", 9) == 0)
- {
- /* CDATA handling */
- p->xml += 9;
- data = p->xml;
- i = 0;
- while(memcmp(p->xml, "]]>", 3) != 0)
- {
- i++; p->xml++;
- if ((p->xml + 3) >= p->xmlend)
- return;
- }
- if(i>0 && p->datafunc)
- p->datafunc(p->data, data, i);
- while(*p->xml!='<')
- {
- p->xml++;
- if (p->xml >= p->xmlend)
- return;
- }
- }
- else
- {
- while(*p->xml!='<')
- {
- i++; p->xml++;
- if ((p->xml + 1) >= p->xmlend)
- return;
- }
- if(i>0 && p->datafunc && *(p->xml + 1) == '/')
- p->datafunc(p->data, data, i);
- }
- }
- }
- else if(*p->xml == '/')
- {
- i = 0; elementname = ++p->xml;
- if (p->xml >= p->xmlend)
- return;
- while((*p->xml != '>'))
- {
- i++; p->xml++;
- if (p->xml >= p->xmlend)
- return;
- }
- if(p->endeltfunc)
- p->endeltfunc(p->data, elementname, i);
- p->xml++;
- }
- }
- else
- {
- p->xml++;
- }
- }
-}
-
-/* the parser must be initialized before calling this function */
-void parsexml(struct xmlparser * parser)
-{
- parser->xml = parser->xmlstart;
- parser->xmlend = parser->xmlstart + parser->xmlsize;
- parseelt(parser);
-}
-
-
+++ /dev/null
-/* $Id: minixml.h,v 1.7 2012/09/27 15:42:10 nanard Exp $ */
-/* minimal xml parser
- *
- * Project : miniupnp
- * Website : http://miniupnp.free.fr/
- * Author : Thomas Bernard
- * Copyright (c) 2005 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-#ifndef MINIXML_H_INCLUDED
-#define MINIXML_H_INCLUDED
-#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
-
-/* if a callback function pointer is set to NULL,
- * the function is not called */
-struct xmlparser {
- const char *xmlstart;
- const char *xmlend;
- const char *xml; /* pointer to current character */
- int xmlsize;
- void * data;
- void (*starteltfunc) (void *, const char *, int);
- void (*endeltfunc) (void *, const char *, int);
- void (*datafunc) (void *, const char *, int);
- void (*attfunc) (void *, const char *, int, const char *, int);
-};
-
-/* parsexml()
- * the xmlparser structure must be initialized before the call
- * the following structure members have to be initialized :
- * xmlstart, xmlsize, data, *func
- * xml is for internal usage, xmlend is computed automatically */
-void parsexml(struct xmlparser *);
-
-#endif
-
+++ /dev/null
-/* $Id: portlistingparse.c,v 1.6 2012/05/29 10:26:51 nanard Exp $ */
-/* MiniUPnP project
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * (c) 2011 Thomas Bernard
- * This software is subject to the conditions detailed
- * in the LICENCE file provided within the distribution */
-#include <string.h>
-#include <stdlib.h>
-#include "portlistingparse.h"
-#include "minixml.h"
-
-/* list of the elements */
-static const struct {
- const portMappingElt code;
- const char * const str;
-} elements[] = {
- { PortMappingEntry, "PortMappingEntry"},
- { NewRemoteHost, "NewRemoteHost"},
- { NewExternalPort, "NewExternalPort"},
- { NewProtocol, "NewProtocol"},
- { NewInternalPort, "NewInternalPort"},
- { NewInternalClient, "NewInternalClient"},
- { NewEnabled, "NewEnabled"},
- { NewDescription, "NewDescription"},
- { NewLeaseTime, "NewLeaseTime"},
- { PortMappingEltNone, NULL}
-};
-
-/* Helper function */
-static UNSIGNED_INTEGER
-atoui(const char * p, int l)
-{
- UNSIGNED_INTEGER r = 0;
- while(l > 0 && *p)
- {
- if(*p >= '0' && *p <= '9')
- r = r*10 + (*p - '0');
- else
- break;
- p++;
- l--;
- }
- return r;
-}
-
-/* Start element handler */
-static void
-startelt(void * d, const char * name, int l)
-{
- int i;
- struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
- pdata->curelt = PortMappingEltNone;
- for(i = 0; elements[i].str; i++)
- {
- if(memcmp(name, elements[i].str, l) == 0)
- {
- pdata->curelt = elements[i].code;
- break;
- }
- }
- if(pdata->curelt == PortMappingEntry)
- {
- struct PortMapping * pm;
- pm = calloc(1, sizeof(struct PortMapping));
- LIST_INSERT_HEAD( &(pdata->head), pm, entries);
- }
-}
-
-/* End element handler */
-static void
-endelt(void * d, const char * name, int l)
-{
- struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
- (void)name;
- (void)l;
- pdata->curelt = PortMappingEltNone;
-}
-
-/* Data handler */
-static void
-data(void * d, const char * data, int l)
-{
- struct PortMapping * pm;
- struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
- pm = pdata->head.lh_first;
- if(!pm)
- return;
- if(l > 63)
- l = 63;
- switch(pdata->curelt)
- {
- case NewRemoteHost:
- memcpy(pm->remoteHost, data, l);
- pm->remoteHost[l] = '\0';
- break;
- case NewExternalPort:
- pm->externalPort = (unsigned short)atoui(data, l);
- break;
- case NewProtocol:
- if(l > 3)
- l = 3;
- memcpy(pm->protocol, data, l);
- pm->protocol[l] = '\0';
- break;
- case NewInternalPort:
- pm->internalPort = (unsigned short)atoui(data, l);
- break;
- case NewInternalClient:
- memcpy(pm->internalClient, data, l);
- pm->internalClient[l] = '\0';
- break;
- case NewEnabled:
- pm->enabled = (unsigned char)atoui(data, l);
- break;
- case NewDescription:
- memcpy(pm->description, data, l);
- pm->description[l] = '\0';
- break;
- case NewLeaseTime:
- pm->leaseTime = atoui(data, l);
- break;
- default:
- break;
- }
-}
-
-
-/* Parse the PortMappingList XML document for IGD version 2
- */
-void
-ParsePortListing(const char * buffer, int bufsize,
- struct PortMappingParserData * pdata)
-{
- struct xmlparser parser;
-
- memset(pdata, 0, sizeof(struct PortMappingParserData));
- LIST_INIT(&(pdata->head));
- /* init xmlparser */
- parser.xmlstart = buffer;
- parser.xmlsize = bufsize;
- parser.data = pdata;
- parser.starteltfunc = startelt;
- parser.endeltfunc = endelt;
- parser.datafunc = data;
- parser.attfunc = 0;
- parsexml(&parser);
-}
-
-void
-FreePortListing(struct PortMappingParserData * pdata)
-{
- struct PortMapping * pm;
- while((pm = pdata->head.lh_first) != NULL)
- {
- LIST_REMOVE(pm, entries);
- free(pm);
- }
-}
-
+++ /dev/null
-/* $Id: portlistingparse.h,v 1.7 2012/09/27 15:42:10 nanard Exp $ */
-/* MiniUPnP project
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * (c) 2011-2012 Thomas Bernard
- * This software is subject to the conditions detailed
- * in the LICENCE file provided within the distribution */
-#ifndef PORTLISTINGPARSE_H_INCLUDED
-#define PORTLISTINGPARSE_H_INCLUDED
-
-#include "declspec.h"
-/* for the definition of UNSIGNED_INTEGER */
-#include "miniupnpctypes.h"
-
-#if defined(NO_SYS_QUEUE_H) || defined(_WIN32) || defined(__HAIKU__)
-#include "bsdqueue.h"
-#else
-#include <sys/queue.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* sample of PortMappingEntry :
- <p:PortMappingEntry>
- <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
- <p:NewExternalPort>2345</p:NewExternalPort>
- <p:NewProtocol>TCP</p:NewProtocol>
- <p:NewInternalPort>2345</p:NewInternalPort>
- <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
- <p:NewEnabled>1</p:NewEnabled>
- <p:NewDescription>dooom</p:NewDescription>
- <p:NewLeaseTime>345</p:NewLeaseTime>
- </p:PortMappingEntry>
- */
-typedef enum { PortMappingEltNone,
- PortMappingEntry, NewRemoteHost,
- NewExternalPort, NewProtocol,
- NewInternalPort, NewInternalClient,
- NewEnabled, NewDescription,
- NewLeaseTime } portMappingElt;
-
-struct PortMapping {
- LIST_ENTRY(PortMapping) entries;
- UNSIGNED_INTEGER leaseTime;
- unsigned short externalPort;
- unsigned short internalPort;
- char remoteHost[64];
- char internalClient[64];
- char description[64];
- char protocol[4];
- unsigned char enabled;
-};
-
-struct PortMappingParserData {
- LIST_HEAD(portmappinglisthead, PortMapping) head;
- portMappingElt curelt;
-};
-
-LIBSPEC void
-ParsePortListing(const char * buffer, int bufsize,
- struct PortMappingParserData * pdata);
-
-LIBSPEC void
-FreePortListing(struct PortMappingParserData * pdata);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/* $Id: receivedata.c,v 1.5 2013/10/07 09:48:36 nanard Exp $ */
-/* Project : miniupnp
- * Website : http://miniupnp.free.fr/
- * Author : Thomas Bernard
- * Copyright (c) 2011-2012 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution. */
-
-#include <stdio.h>
-#ifdef _WIN32
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#else
-#include <unistd.h>
-#if defined(__amigaos__) && !defined(__amigaos4__)
-#define socklen_t int
-#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
-#include <sys/select.h>
-#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
-#include <sys/socket.h>
-#include <netinet/in.h>
-#if !defined(__amigaos__) && !defined(__amigaos4__)
-#include <poll.h>
-#endif
-#include <errno.h>
-#define MINIUPNPC_IGNORE_EINTR
-#endif
-
-#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
-#else
-#define PRINT_SOCKET_ERROR(x) perror(x)
-#endif
-
-#include "receivedata.h"
-
-int
-receivedata(int socket,
- char * data, int length,
- int timeout, unsigned int * scope_id)
-{
-#ifdef MINIUPNPC_GET_SRC_ADDR
-#ifdef DEBUG
- /* to shut up valgrind about uninit value */
- struct sockaddr_storage src_addr = {0};
-#else
- struct sockaddr_storage src_addr;
-#endif
- socklen_t src_addr_len = sizeof(src_addr);
-#endif
- int n;
-#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
- /* using poll */
- struct pollfd fds[1]; /* for the poll */
-#ifdef MINIUPNPC_IGNORE_EINTR
- do {
-#endif
- fds[0].fd = socket;
- fds[0].events = POLLIN;
- n = poll(fds, 1, timeout);
-#ifdef MINIUPNPC_IGNORE_EINTR
- } while(n < 0 && errno == EINTR);
-#endif
- if(n < 0) {
- PRINT_SOCKET_ERROR("poll");
- return -1;
- } else if(n == 0) {
- /* timeout */
- return 0;
- }
-#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
- /* using select under _WIN32 and amigaos */
- fd_set socketSet;
- TIMEVAL timeval;
- FD_ZERO(&socketSet);
- FD_SET(socket, &socketSet);
- timeval.tv_sec = timeout / 1000;
- timeval.tv_usec = (timeout % 1000) * 1000;
- n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
- if(n < 0) {
- PRINT_SOCKET_ERROR("select");
- return -1;
- } else if(n == 0) {
- return 0;
- }
-#endif
-#ifdef MINIUPNPC_GET_SRC_ADDR
- n = recvfrom(socket, data, length, 0,
- (struct sockaddr *)&src_addr, &src_addr_len);
-#else
- n = recv(socket, data, length, 0);
-#endif
- if(n<0) {
- PRINT_SOCKET_ERROR("recv");
- }
-#ifdef MINIUPNPC_GET_SRC_ADDR
- if (src_addr.ss_family == AF_INET6) {
- const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr;
-#ifdef DEBUG
- printf("scope_id=%u\n", src_addr6->sin6_scope_id);
-#endif
- if(scope_id)
- *scope_id = src_addr6->sin6_scope_id;
- }
-#endif
- return n;
-}
-
-
+++ /dev/null
-/* $Id: receivedata.h,v 1.4 2012/09/27 15:42:10 nanard Exp $ */
-/* Project: miniupnp
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * Author: Thomas Bernard
- * Copyright (c) 2011-2012 Thomas Bernard
- * This software is subjects to the conditions detailed
- * in the LICENCE file provided within this distribution */
-#ifndef RECEIVEDATA_H_INCLUDED
-#define RECEIVEDATA_H_INCLUDED
-
-/* Reads data from the specified socket.
- * Returns the number of bytes read if successful, zero if no bytes were
- * read or if we timed out. Returns negative if there was an error. */
-int receivedata(int socket,
- char * data, int length,
- int timeout, unsigned int * scope_id);
-
-#endif
-
+++ /dev/null
-#! /bin/sh
-# $Id: updateminiupnpcstrings.sh,v 1.7 2011/01/04 11:41:53 nanard Exp $
-# project miniupnp : http://miniupnp.free.fr/
-#Â (c) 2009 Thomas Bernard
-
-VERSION_FILE="$1"
-TEMPLATE_FILE="$2"
-FILE="$3"
-TMPFILE="$3.tmp"
-
-# detecting the OS name and version
-OS_NAME=`uname -s`
-OS_VERSION=`uname -r`
-if [ -f /etc/debian_version ]; then
- OS_NAME=Debian
- OS_VERSION=`cat /etc/debian_version`
-fi
-# use lsb_release (Linux Standard Base) when available
-LSB_RELEASE=`which lsb_release`
-if [ 0 -eq $? -a -x "${LSB_RELEASE}" ]; then
- OS_NAME=`${LSB_RELEASE} -i -s`
- OS_VERSION=`${LSB_RELEASE} -r -s`
- case $OS_NAME in
- Debian)
- #OS_VERSION=`${LSB_RELEASE} -c -s`
- ;;
- Ubuntu)
- #OS_VERSION=`${LSB_RELEASE} -c -s`
- ;;
- esac
-fi
-
-# on AmigaOS 3, uname -r returns "unknown", so we use uname -v
-if [ "$OS_NAME" = "AmigaOS" ]; then
- if [ "$OS_VERSION" = "unknown" ]; then
- OS_VERSION=`uname -v`
- fi
-fi
-
-echo "Detected OS [$OS_NAME] version [$OS_VERSION]"
-MINIUPNPC_VERSION=`cat "${VERSION_FILE}"`
-echo "MiniUPnPc version [${MINIUPNPC_VERSION}]"
-
-EXPR="s|OS_STRING \".*\"|OS_STRING \"${OS_NAME}/${OS_VERSION}\"|"
-#echo $EXPR
-test -f "${FILE}.in"
-echo "setting OS_STRING macro value to ${OS_NAME}/${OS_VERSION} in $FILE."
-sed -e "$EXPR" < "$TEMPLATE_FILE" > "$TMPFILE"
-
-EXPR="s|MINIUPNPC_VERSION_STRING \".*\"|MINIUPNPC_VERSION_STRING \"${MINIUPNPC_VERSION}\"|"
-echo "setting MINIUPNPC_VERSION_STRING macro value to ${MINIUPNPC_VERSION} in $FILE."
-sed -e "$EXPR" < "$TMPFILE" > "$FILE"
-rm "$TMPFILE"
-
+++ /dev/null
-/* $Id: upnpcommands.c,v 1.42 2014/01/31 13:18:25 nanard Exp $ */
-/* Project : miniupnp
- * Author : Thomas Bernard
- * Copyright (c) 2005-2012 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided in this distribution.
- * */
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include "upnpcommands.h"
-#include "miniupnpc.h"
-#include "portlistingparse.h"
-
-static UNSIGNED_INTEGER
-my_atoui(const char * s)
-{
- return s ? ((UNSIGNED_INTEGER)STRTOUI(s, NULL, 0)) : 0;
-}
-
-/*
- * */
-LIBSPEC UNSIGNED_INTEGER
-UPNP_GetTotalBytesSent(const char * controlURL,
- const char * servicetype)
-{
- struct NameValueParserData pdata;
- char * buffer;
- int bufsize;
- unsigned int r = 0;
- char * p;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetTotalBytesSent", 0, &bufsize))) {
- return UPNPCOMMAND_HTTP_ERROR;
- }
- ParseNameValue(buffer, bufsize, &pdata);
- /*DisplayNameValueList(buffer, bufsize);*/
- free(buffer); buffer = NULL;
- p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent");
- r = my_atoui(p);
- ClearNameValueList(&pdata);
- return r;
-}
-
-/*
- * */
-LIBSPEC UNSIGNED_INTEGER
-UPNP_GetTotalBytesReceived(const char * controlURL,
- const char * servicetype)
-{
- struct NameValueParserData pdata;
- char * buffer;
- int bufsize;
- unsigned int r = 0;
- char * p;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetTotalBytesReceived", 0, &bufsize))) {
- return UPNPCOMMAND_HTTP_ERROR;
- }
- ParseNameValue(buffer, bufsize, &pdata);
- /*DisplayNameValueList(buffer, bufsize);*/
- free(buffer); buffer = NULL;
- p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived");
- r = my_atoui(p);
- ClearNameValueList(&pdata);
- return r;
-}
-
-/*
- * */
-LIBSPEC UNSIGNED_INTEGER
-UPNP_GetTotalPacketsSent(const char * controlURL,
- const char * servicetype)
-{
- struct NameValueParserData pdata;
- char * buffer;
- int bufsize;
- unsigned int r = 0;
- char * p;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetTotalPacketsSent", 0, &bufsize))) {
- return UPNPCOMMAND_HTTP_ERROR;
- }
- ParseNameValue(buffer, bufsize, &pdata);
- /*DisplayNameValueList(buffer, bufsize);*/
- free(buffer); buffer = NULL;
- p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent");
- r = my_atoui(p);
- ClearNameValueList(&pdata);
- return r;
-}
-
-/*
- * */
-LIBSPEC UNSIGNED_INTEGER
-UPNP_GetTotalPacketsReceived(const char * controlURL,
- const char * servicetype)
-{
- struct NameValueParserData pdata;
- char * buffer;
- int bufsize;
- unsigned int r = 0;
- char * p;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetTotalPacketsReceived", 0, &bufsize))) {
- return UPNPCOMMAND_HTTP_ERROR;
- }
- ParseNameValue(buffer, bufsize, &pdata);
- /*DisplayNameValueList(buffer, bufsize);*/
- free(buffer); buffer = NULL;
- p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived");
- r = my_atoui(p);
- ClearNameValueList(&pdata);
- return r;
-}
-
-/* UPNP_GetStatusInfo() call the corresponding UPNP method
- * returns the current status and uptime */
-LIBSPEC int
-UPNP_GetStatusInfo(const char * controlURL,
- const char * servicetype,
- char * status,
- unsigned int * uptime,
- char * lastconnerror)
-{
- struct NameValueParserData pdata;
- char * buffer;
- int bufsize;
- char * p;
- char * up;
- char * err;
- int ret = UPNPCOMMAND_UNKNOWN_ERROR;
-
- if(!status && !uptime)
- return UPNPCOMMAND_INVALID_ARGS;
-
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetStatusInfo", 0, &bufsize))) {
- return UPNPCOMMAND_HTTP_ERROR;
- }
- ParseNameValue(buffer, bufsize, &pdata);
- /*DisplayNameValueList(buffer, bufsize);*/
- free(buffer); buffer = NULL;
- up = GetValueFromNameValueList(&pdata, "NewUptime");
- p = GetValueFromNameValueList(&pdata, "NewConnectionStatus");
- err = GetValueFromNameValueList(&pdata, "NewLastConnectionError");
- if(p && up)
- ret = UPNPCOMMAND_SUCCESS;
-
- if(status) {
- if(p){
- strncpy(status, p, 64 );
- status[63] = '\0';
- }else
- status[0]= '\0';
- }
-
- if(uptime) {
- if(up)
- sscanf(up,"%u",uptime);
- else
- uptime = 0;
- }
-
- if(lastconnerror) {
- if(err) {
- strncpy(lastconnerror, err, 64 );
- lastconnerror[63] = '\0';
- } else
- lastconnerror[0] = '\0';
- }
-
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p) {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &ret);
- }
- ClearNameValueList(&pdata);
- return ret;
-}
-
-/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method
- * returns the connection type */
-LIBSPEC int
-UPNP_GetConnectionTypeInfo(const char * controlURL,
- const char * servicetype,
- char * connectionType)
-{
- struct NameValueParserData pdata;
- char * buffer;
- int bufsize;
- char * p;
- int ret = UPNPCOMMAND_UNKNOWN_ERROR;
-
- if(!connectionType)
- return UPNPCOMMAND_INVALID_ARGS;
-
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetConnectionTypeInfo", 0, &bufsize))) {
- return UPNPCOMMAND_HTTP_ERROR;
- }
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
- p = GetValueFromNameValueList(&pdata, "NewConnectionType");
- /*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/
- /* PossibleConnectionTypes will have several values.... */
- if(p) {
- strncpy(connectionType, p, 64 );
- connectionType[63] = '\0';
- ret = UPNPCOMMAND_SUCCESS;
- } else
- connectionType[0] = '\0';
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p) {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &ret);
- }
- ClearNameValueList(&pdata);
- return ret;
-}
-
-/* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method.
- * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth.
- * One of the values can be null
- * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only
- * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */
-LIBSPEC int
-UPNP_GetLinkLayerMaxBitRates(const char * controlURL,
- const char * servicetype,
- unsigned int * bitrateDown,
- unsigned int * bitrateUp)
-{
- struct NameValueParserData pdata;
- char * buffer;
- int bufsize;
- int ret = UPNPCOMMAND_UNKNOWN_ERROR;
- char * down;
- char * up;
- char * p;
-
- if(!bitrateDown && !bitrateUp)
- return UPNPCOMMAND_INVALID_ARGS;
-
- /* shouldn't we use GetCommonLinkProperties ? */
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetCommonLinkProperties", 0, &bufsize))) {
- /*"GetLinkLayerMaxBitRates", 0, &bufsize);*/
- return UPNPCOMMAND_HTTP_ERROR;
- }
- /*DisplayNameValueList(buffer, bufsize);*/
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
- /*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/
- /*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate");*/
- down = GetValueFromNameValueList(&pdata, "NewLayer1DownstreamMaxBitRate");
- up = GetValueFromNameValueList(&pdata, "NewLayer1UpstreamMaxBitRate");
- /*GetValueFromNameValueList(&pdata, "NewWANAccessType");*/
- /*GetValueFromNameValueList(&pdata, "NewPhysicalLinkStatus");*/
- if(down && up)
- ret = UPNPCOMMAND_SUCCESS;
-
- if(bitrateDown) {
- if(down)
- sscanf(down,"%u",bitrateDown);
- else
- *bitrateDown = 0;
- }
-
- if(bitrateUp) {
- if(up)
- sscanf(up,"%u",bitrateUp);
- else
- *bitrateUp = 0;
- }
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p) {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &ret);
- }
- ClearNameValueList(&pdata);
- return ret;
-}
-
-
-/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
- * if the third arg is not null the value is copied to it.
- * at least 16 bytes must be available
- *
- * Return values :
- * 0 : SUCCESS
- * NON ZERO : ERROR Either an UPnP error code or an unknown error.
- *
- * 402 Invalid Args - See UPnP Device Architecture section on Control.
- * 501 Action Failed - See UPnP Device Architecture section on Control.
- */
-LIBSPEC int
-UPNP_GetExternalIPAddress(const char * controlURL,
- const char * servicetype,
- char * extIpAdd)
-{
- struct NameValueParserData pdata;
- char * buffer;
- int bufsize;
- char * p;
- int ret = UPNPCOMMAND_UNKNOWN_ERROR;
-
- if(!extIpAdd || !controlURL || !servicetype)
- return UPNPCOMMAND_INVALID_ARGS;
-
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetExternalIPAddress", 0, &bufsize))) {
- return UPNPCOMMAND_HTTP_ERROR;
- }
- /*DisplayNameValueList(buffer, bufsize);*/
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
- /*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/
- p = GetValueFromNameValueList(&pdata, "NewExternalIPAddress");
- if(p) {
- strncpy(extIpAdd, p, 16 );
- extIpAdd[15] = '\0';
- ret = UPNPCOMMAND_SUCCESS;
- } else
- extIpAdd[0] = '\0';
-
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p) {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &ret);
- }
-
- ClearNameValueList(&pdata);
- return ret;
-}
-
-LIBSPEC int
-UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
- const char * extPort,
- const char * inPort,
- const char * inClient,
- const char * desc,
- const char * proto,
- const char * remoteHost,
- const char * leaseDuration)
-{
- struct UPNParg * AddPortMappingArgs;
- char * buffer;
- int bufsize;
- struct NameValueParserData pdata;
- const char * resVal;
- int ret;
-
- if(!inPort || !inClient || !proto || !extPort)
- return UPNPCOMMAND_INVALID_ARGS;
-
- AddPortMappingArgs = calloc(9, sizeof(struct UPNParg));
- AddPortMappingArgs[0].elt = "NewRemoteHost";
- AddPortMappingArgs[0].val = remoteHost;
- AddPortMappingArgs[1].elt = "NewExternalPort";
- AddPortMappingArgs[1].val = extPort;
- AddPortMappingArgs[2].elt = "NewProtocol";
- AddPortMappingArgs[2].val = proto;
- AddPortMappingArgs[3].elt = "NewInternalPort";
- AddPortMappingArgs[3].val = inPort;
- AddPortMappingArgs[4].elt = "NewInternalClient";
- AddPortMappingArgs[4].val = inClient;
- AddPortMappingArgs[5].elt = "NewEnabled";
- AddPortMappingArgs[5].val = "1";
- AddPortMappingArgs[6].elt = "NewPortMappingDescription";
- AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
- AddPortMappingArgs[7].elt = "NewLeaseDuration";
- AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "AddPortMapping", AddPortMappingArgs,
- &bufsize))) {
- free(AddPortMappingArgs);
- return UPNPCOMMAND_HTTP_ERROR;
- }
- /*DisplayNameValueList(buffer, bufsize);*/
- /*buffer[bufsize] = '\0';*/
- /*puts(buffer);*/
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
- resVal = GetValueFromNameValueList(&pdata, "errorCode");
- if(resVal) {
- /*printf("AddPortMapping errorCode = '%s'\n", resVal); */
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(resVal, "%d", &ret);
- } else {
- ret = UPNPCOMMAND_SUCCESS;
- }
- ClearNameValueList(&pdata);
- free(AddPortMappingArgs);
- return ret;
-}
-
-LIBSPEC int
-UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
- const char * extPort, const char * proto,
- const char * remoteHost)
-{
- /*struct NameValueParserData pdata;*/
- struct UPNParg * DeletePortMappingArgs;
- char * buffer;
- int bufsize;
- struct NameValueParserData pdata;
- const char * resVal;
- int ret;
-
- if(!extPort || !proto)
- return UPNPCOMMAND_INVALID_ARGS;
-
- DeletePortMappingArgs = calloc(4, sizeof(struct UPNParg));
- DeletePortMappingArgs[0].elt = "NewRemoteHost";
- DeletePortMappingArgs[0].val = remoteHost;
- DeletePortMappingArgs[1].elt = "NewExternalPort";
- DeletePortMappingArgs[1].val = extPort;
- DeletePortMappingArgs[2].elt = "NewProtocol";
- DeletePortMappingArgs[2].val = proto;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "DeletePortMapping",
- DeletePortMappingArgs, &bufsize))) {
- free(DeletePortMappingArgs);
- return UPNPCOMMAND_HTTP_ERROR;
- }
- /*DisplayNameValueList(buffer, bufsize);*/
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
- resVal = GetValueFromNameValueList(&pdata, "errorCode");
- if(resVal) {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(resVal, "%d", &ret);
- } else {
- ret = UPNPCOMMAND_SUCCESS;
- }
- ClearNameValueList(&pdata);
- free(DeletePortMappingArgs);
- return ret;
-}
-
-LIBSPEC int
-UPNP_GetGenericPortMappingEntry(const char * controlURL,
- const char * servicetype,
- const char * index,
- char * extPort,
- char * intClient,
- char * intPort,
- char * protocol,
- char * desc,
- char * enabled,
- char * rHost,
- char * duration)
-{
- struct NameValueParserData pdata;
- struct UPNParg * GetPortMappingArgs;
- char * buffer;
- int bufsize;
- char * p;
- int r = UPNPCOMMAND_UNKNOWN_ERROR;
- if(!index)
- return UPNPCOMMAND_INVALID_ARGS;
- intClient[0] = '\0';
- intPort[0] = '\0';
- GetPortMappingArgs = calloc(2, sizeof(struct UPNParg));
- GetPortMappingArgs[0].elt = "NewPortMappingIndex";
- GetPortMappingArgs[0].val = index;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetGenericPortMappingEntry",
- GetPortMappingArgs, &bufsize))) {
- free(GetPortMappingArgs);
- return UPNPCOMMAND_HTTP_ERROR;
- }
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
-
- p = GetValueFromNameValueList(&pdata, "NewRemoteHost");
- if(p && rHost)
- {
- strncpy(rHost, p, 64);
- rHost[63] = '\0';
- }
- p = GetValueFromNameValueList(&pdata, "NewExternalPort");
- if(p && extPort)
- {
- strncpy(extPort, p, 6);
- extPort[5] = '\0';
- r = UPNPCOMMAND_SUCCESS;
- }
- p = GetValueFromNameValueList(&pdata, "NewProtocol");
- if(p && protocol)
- {
- strncpy(protocol, p, 4);
- protocol[3] = '\0';
- }
- p = GetValueFromNameValueList(&pdata, "NewInternalClient");
- if(p && intClient)
- {
- strncpy(intClient, p, 16);
- intClient[15] = '\0';
- r = 0;
- }
- p = GetValueFromNameValueList(&pdata, "NewInternalPort");
- if(p && intPort)
- {
- strncpy(intPort, p, 6);
- intPort[5] = '\0';
- }
- p = GetValueFromNameValueList(&pdata, "NewEnabled");
- if(p && enabled)
- {
- strncpy(enabled, p, 4);
- enabled[3] = '\0';
- }
- p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
- if(p && desc)
- {
- strncpy(desc, p, 80);
- desc[79] = '\0';
- }
- p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
- if(p && duration)
- {
- strncpy(duration, p, 16);
- duration[15] = '\0';
- }
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p) {
- r = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &r);
- }
- ClearNameValueList(&pdata);
- free(GetPortMappingArgs);
- return r;
-}
-
-LIBSPEC int
-UPNP_GetPortMappingNumberOfEntries(const char * controlURL,
- const char * servicetype,
- unsigned int * numEntries)
-{
- struct NameValueParserData pdata;
- char * buffer;
- int bufsize;
- char* p;
- int ret = UPNPCOMMAND_UNKNOWN_ERROR;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetPortMappingNumberOfEntries", 0,
- &bufsize))) {
- return UPNPCOMMAND_HTTP_ERROR;
- }
-#ifdef DEBUG
- DisplayNameValueList(buffer, bufsize);
-#endif
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
-
- p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries");
- if(numEntries && p) {
- *numEntries = 0;
- sscanf(p, "%u", numEntries);
- ret = UPNPCOMMAND_SUCCESS;
- }
-
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p) {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &ret);
- }
-
- ClearNameValueList(&pdata);
- return ret;
-}
-
-/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
- * the result is returned in the intClient and intPort strings
- * please provide 16 and 6 bytes of data */
-LIBSPEC int
-UPNP_GetSpecificPortMappingEntry(const char * controlURL,
- const char * servicetype,
- const char * extPort,
- const char * proto,
- const char * remoteHost,
- char * intClient,
- char * intPort,
- char * desc,
- char * enabled,
- char * leaseDuration)
-{
- struct NameValueParserData pdata;
- struct UPNParg * GetPortMappingArgs;
- char * buffer;
- int bufsize;
- char * p;
- int ret = UPNPCOMMAND_UNKNOWN_ERROR;
-
- if(!intPort || !intClient || !extPort || !proto)
- return UPNPCOMMAND_INVALID_ARGS;
-
- GetPortMappingArgs = calloc(4, sizeof(struct UPNParg));
- GetPortMappingArgs[0].elt = "NewRemoteHost";
- GetPortMappingArgs[0].val = remoteHost;
- GetPortMappingArgs[1].elt = "NewExternalPort";
- GetPortMappingArgs[1].val = extPort;
- GetPortMappingArgs[2].elt = "NewProtocol";
- GetPortMappingArgs[2].val = proto;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetSpecificPortMappingEntry",
- GetPortMappingArgs, &bufsize))) {
- free(GetPortMappingArgs);
- return UPNPCOMMAND_HTTP_ERROR;
- }
- /*DisplayNameValueList(buffer, bufsize);*/
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
-
- p = GetValueFromNameValueList(&pdata, "NewInternalClient");
- if(p) {
- strncpy(intClient, p, 16);
- intClient[15] = '\0';
- ret = UPNPCOMMAND_SUCCESS;
- } else
- intClient[0] = '\0';
-
- p = GetValueFromNameValueList(&pdata, "NewInternalPort");
- if(p) {
- strncpy(intPort, p, 6);
- intPort[5] = '\0';
- } else
- intPort[0] = '\0';
-
- p = GetValueFromNameValueList(&pdata, "NewEnabled");
- if(p && enabled) {
- strncpy(enabled, p, 4);
- enabled[3] = '\0';
- }
-
- p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription");
- if(p && desc) {
- strncpy(desc, p, 80);
- desc[79] = '\0';
- }
-
- p = GetValueFromNameValueList(&pdata, "NewLeaseDuration");
- if(p && leaseDuration)
- {
- strncpy(leaseDuration, p, 16);
- leaseDuration[15] = '\0';
- }
-
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p) {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &ret);
- }
-
- ClearNameValueList(&pdata);
- free(GetPortMappingArgs);
- return ret;
-}
-
-/* UPNP_GetListOfPortMappings()
- *
- * Possible UPNP Error codes :
- * 606 Action not Authorized
- * 730 PortMappingNotFound - no port mapping is found in the specified range.
- * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
- * consistent.
- */
-LIBSPEC int
-UPNP_GetListOfPortMappings(const char * controlURL,
- const char * servicetype,
- const char * startPort,
- const char * endPort,
- const char * protocol,
- const char * numberOfPorts,
- struct PortMappingParserData * data)
-{
- struct NameValueParserData pdata;
- struct UPNParg * GetListOfPortMappingsArgs;
- const char * p;
- char * buffer;
- int bufsize;
- int ret = UPNPCOMMAND_UNKNOWN_ERROR;
-
- if(!startPort || !endPort || !protocol)
- return UPNPCOMMAND_INVALID_ARGS;
-
- GetListOfPortMappingsArgs = calloc(6, sizeof(struct UPNParg));
- GetListOfPortMappingsArgs[0].elt = "NewStartPort";
- GetListOfPortMappingsArgs[0].val = startPort;
- GetListOfPortMappingsArgs[1].elt = "NewEndPort";
- GetListOfPortMappingsArgs[1].val = endPort;
- GetListOfPortMappingsArgs[2].elt = "NewProtocol";
- GetListOfPortMappingsArgs[2].val = protocol;
- GetListOfPortMappingsArgs[3].elt = "NewManage";
- GetListOfPortMappingsArgs[3].val = "1";
- GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts";
- GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000";
-
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetListOfPortMappings",
- GetListOfPortMappingsArgs, &bufsize))) {
- free(GetListOfPortMappingsArgs);
- return UPNPCOMMAND_HTTP_ERROR;
- }
- free(GetListOfPortMappingsArgs);
-
- /*DisplayNameValueList(buffer, bufsize);*/
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
-
- /*p = GetValueFromNameValueList(&pdata, "NewPortListing");*/
- /*if(p) {
- printf("NewPortListing : %s\n", p);
- }*/
- /*printf("NewPortListing(%d chars) : %s\n",
- pdata.portListingLength, pdata.portListing);*/
- if(pdata.portListing)
- {
- /*struct PortMapping * pm;
- int i = 0;*/
- ParsePortListing(pdata.portListing, pdata.portListingLength,
- data);
- ret = UPNPCOMMAND_SUCCESS;
- /*
- for(pm = data->head.lh_first; pm != NULL; pm = pm->entries.le_next)
- {
- printf("%2d %s %5hu->%s:%-5hu '%s' '%s'\n",
- i, pm->protocol, pm->externalPort, pm->internalClient,
- pm->internalPort,
- pm->description, pm->remoteHost);
- i++;
- }
- */
- /*FreePortListing(&data);*/
- }
-
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p) {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &ret);
- }
- ClearNameValueList(&pdata);
-
- /*printf("%.*s", bufsize, buffer);*/
-
- return ret;
-}
-
-/* IGD:2, functions for service WANIPv6FirewallControl:1 */
-LIBSPEC int
-UPNP_GetFirewallStatus(const char * controlURL,
- const char * servicetype,
- int * firewallEnabled,
- int * inboundPinholeAllowed)
-{
- struct NameValueParserData pdata;
- char * buffer;
- int bufsize;
- char * fe, *ipa, *p;
- int ret = UPNPCOMMAND_UNKNOWN_ERROR;
-
- if(!firewallEnabled || !inboundPinholeAllowed)
- return UPNPCOMMAND_INVALID_ARGS;
-
- buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetFirewallStatus", 0, &bufsize);
- if(!buffer) {
- return UPNPCOMMAND_HTTP_ERROR;
- }
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
- fe = GetValueFromNameValueList(&pdata, "FirewallEnabled");
- ipa = GetValueFromNameValueList(&pdata, "InboundPinholeAllowed");
- if(ipa && fe)
- ret = UPNPCOMMAND_SUCCESS;
- if(fe)
- *firewallEnabled = my_atoui(fe);
- /*else
- *firewallEnabled = 0;*/
- if(ipa)
- *inboundPinholeAllowed = my_atoui(ipa);
- /*else
- *inboundPinholeAllowed = 0;*/
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p)
- {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &ret);
- }
- ClearNameValueList(&pdata);
- return ret;
-}
-
-LIBSPEC int
-UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
- const char * remoteHost,
- const char * remotePort,
- const char * intClient,
- const char * intPort,
- const char * proto,
- int * opTimeout)
-{
- struct UPNParg * GetOutboundPinholeTimeoutArgs;
- char * buffer;
- int bufsize;
- struct NameValueParserData pdata;
- const char * resVal;
- char * p;
- int ret;
-
- if(!intPort || !intClient || !proto || !remotePort || !remoteHost)
- return UPNPCOMMAND_INVALID_ARGS;
-
- GetOutboundPinholeTimeoutArgs = calloc(6, sizeof(struct UPNParg));
- GetOutboundPinholeTimeoutArgs[0].elt = "RemoteHost";
- GetOutboundPinholeTimeoutArgs[0].val = remoteHost;
- GetOutboundPinholeTimeoutArgs[1].elt = "RemotePort";
- GetOutboundPinholeTimeoutArgs[1].val = remotePort;
- GetOutboundPinholeTimeoutArgs[2].elt = "Protocol";
- GetOutboundPinholeTimeoutArgs[2].val = proto;
- GetOutboundPinholeTimeoutArgs[3].elt = "InternalPort";
- GetOutboundPinholeTimeoutArgs[3].val = intPort;
- GetOutboundPinholeTimeoutArgs[4].elt = "InternalClient";
- GetOutboundPinholeTimeoutArgs[4].val = intClient;
- buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize);
- if(!buffer)
- return UPNPCOMMAND_HTTP_ERROR;
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
- resVal = GetValueFromNameValueList(&pdata, "errorCode");
- if(resVal)
- {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(resVal, "%d", &ret);
- }
- else
- {
- ret = UPNPCOMMAND_SUCCESS;
- p = GetValueFromNameValueList(&pdata, "OutboundPinholeTimeout");
- if(p)
- *opTimeout = my_atoui(p);
- }
- ClearNameValueList(&pdata);
- free(GetOutboundPinholeTimeoutArgs);
- return ret;
-}
-
-LIBSPEC int
-UPNP_AddPinhole(const char * controlURL, const char * servicetype,
- const char * remoteHost,
- const char * remotePort,
- const char * intClient,
- const char * intPort,
- const char * proto,
- const char * leaseTime,
- char * uniqueID)
-{
- struct UPNParg * AddPinholeArgs;
- char * buffer;
- int bufsize;
- struct NameValueParserData pdata;
- const char * resVal;
- char * p;
- int ret;
-
- if(!intPort || !intClient || !proto || !remoteHost || !remotePort || !leaseTime)
- return UPNPCOMMAND_INVALID_ARGS;
-
- AddPinholeArgs = calloc(7, sizeof(struct UPNParg));
- /* RemoteHost can be wilcarded */
- if(strncmp(remoteHost, "empty", 5)==0)
- {
- AddPinholeArgs[0].elt = "RemoteHost";
- AddPinholeArgs[0].val = "";
- }
- else
- {
- AddPinholeArgs[0].elt = "RemoteHost";
- AddPinholeArgs[0].val = remoteHost;
- }
- AddPinholeArgs[1].elt = "RemotePort";
- AddPinholeArgs[1].val = remotePort;
- AddPinholeArgs[2].elt = "Protocol";
- AddPinholeArgs[2].val = proto;
- AddPinholeArgs[3].elt = "InternalPort";
- AddPinholeArgs[3].val = intPort;
- if(strncmp(intClient, "empty", 5)==0)
- {
- AddPinholeArgs[4].elt = "InternalClient";
- AddPinholeArgs[4].val = "";
- }
- else
- {
- AddPinholeArgs[4].elt = "InternalClient";
- AddPinholeArgs[4].val = intClient;
- }
- AddPinholeArgs[5].elt = "LeaseTime";
- AddPinholeArgs[5].val = leaseTime;
- buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "AddPinhole", AddPinholeArgs, &bufsize);
- if(!buffer)
- return UPNPCOMMAND_HTTP_ERROR;
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
- p = GetValueFromNameValueList(&pdata, "UniqueID");
- if(p)
- {
- strncpy(uniqueID, p, 8);
- uniqueID[7] = '\0';
- }
- resVal = GetValueFromNameValueList(&pdata, "errorCode");
- if(resVal)
- {
- /*printf("AddPortMapping errorCode = '%s'\n", resVal);*/
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(resVal, "%d", &ret);
- }
- else
- {
- ret = UPNPCOMMAND_SUCCESS;
- }
- ClearNameValueList(&pdata);
- free(AddPinholeArgs);
- return ret;
-}
-
-LIBSPEC int
-UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
- const char * uniqueID,
- const char * leaseTime)
-{
- struct UPNParg * UpdatePinholeArgs;
- char * buffer;
- int bufsize;
- struct NameValueParserData pdata;
- const char * resVal;
- int ret;
-
- if(!uniqueID || !leaseTime)
- return UPNPCOMMAND_INVALID_ARGS;
-
- UpdatePinholeArgs = calloc(3, sizeof(struct UPNParg));
- UpdatePinholeArgs[0].elt = "UniqueID";
- UpdatePinholeArgs[0].val = uniqueID;
- UpdatePinholeArgs[1].elt = "NewLeaseTime";
- UpdatePinholeArgs[1].val = leaseTime;
- buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "UpdatePinhole", UpdatePinholeArgs, &bufsize);
- if(!buffer)
- return UPNPCOMMAND_HTTP_ERROR;
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
- resVal = GetValueFromNameValueList(&pdata, "errorCode");
- if(resVal)
- {
- /*printf("AddPortMapping errorCode = '%s'\n", resVal); */
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(resVal, "%d", &ret);
- }
- else
- {
- ret = UPNPCOMMAND_SUCCESS;
- }
- ClearNameValueList(&pdata);
- free(UpdatePinholeArgs);
- return ret;
-}
-
-LIBSPEC int
-UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID)
-{
- /*struct NameValueParserData pdata;*/
- struct UPNParg * DeletePinholeArgs;
- char * buffer;
- int bufsize;
- struct NameValueParserData pdata;
- const char * resVal;
- int ret;
-
- if(!uniqueID)
- return UPNPCOMMAND_INVALID_ARGS;
-
- DeletePinholeArgs = calloc(2, sizeof(struct UPNParg));
- DeletePinholeArgs[0].elt = "UniqueID";
- DeletePinholeArgs[0].val = uniqueID;
- buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "DeletePinhole", DeletePinholeArgs, &bufsize);
- if(!buffer)
- return UPNPCOMMAND_HTTP_ERROR;
- /*DisplayNameValueList(buffer, bufsize);*/
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
- resVal = GetValueFromNameValueList(&pdata, "errorCode");
- if(resVal)
- {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(resVal, "%d", &ret);
- }
- else
- {
- ret = UPNPCOMMAND_SUCCESS;
- }
- ClearNameValueList(&pdata);
- free(DeletePinholeArgs);
- return ret;
-}
-
-LIBSPEC int
-UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
- const char * uniqueID, int * isWorking)
-{
- struct NameValueParserData pdata;
- struct UPNParg * CheckPinholeWorkingArgs;
- char * buffer;
- int bufsize;
- char * p;
- int ret = UPNPCOMMAND_UNKNOWN_ERROR;
-
- if(!uniqueID)
- return UPNPCOMMAND_INVALID_ARGS;
-
- CheckPinholeWorkingArgs = calloc(4, sizeof(struct UPNParg));
- CheckPinholeWorkingArgs[0].elt = "UniqueID";
- CheckPinholeWorkingArgs[0].val = uniqueID;
- buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize);
- if(!buffer)
- return UPNPCOMMAND_HTTP_ERROR;
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
-
- p = GetValueFromNameValueList(&pdata, "IsWorking");
- if(p)
- {
- *isWorking=my_atoui(p);
- ret = UPNPCOMMAND_SUCCESS;
- }
- else
- *isWorking = 0;
-
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p)
- {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &ret);
- }
-
- ClearNameValueList(&pdata);
- free(CheckPinholeWorkingArgs);
- return ret;
-}
-
-LIBSPEC int
-UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
- const char * uniqueID, int * packets)
-{
- struct NameValueParserData pdata;
- struct UPNParg * GetPinholePacketsArgs;
- char * buffer;
- int bufsize;
- char * p;
- int ret = UPNPCOMMAND_UNKNOWN_ERROR;
-
- if(!uniqueID)
- return UPNPCOMMAND_INVALID_ARGS;
-
- GetPinholePacketsArgs = calloc(4, sizeof(struct UPNParg));
- GetPinholePacketsArgs[0].elt = "UniqueID";
- GetPinholePacketsArgs[0].val = uniqueID;
- buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetPinholePackets", GetPinholePacketsArgs, &bufsize);
- if(!buffer)
- return UPNPCOMMAND_HTTP_ERROR;
- ParseNameValue(buffer, bufsize, &pdata);
- free(buffer); buffer = NULL;
-
- p = GetValueFromNameValueList(&pdata, "PinholePackets");
- if(p)
- {
- *packets=my_atoui(p);
- ret = UPNPCOMMAND_SUCCESS;
- }
-
- p = GetValueFromNameValueList(&pdata, "errorCode");
- if(p)
- {
- ret = UPNPCOMMAND_UNKNOWN_ERROR;
- sscanf(p, "%d", &ret);
- }
-
- ClearNameValueList(&pdata);
- free(GetPinholePacketsArgs);
- return ret;
-}
-
-
+++ /dev/null
-/* $Id: upnpcommands.h,v 1.27 2014/02/17 15:38:26 nanard Exp $ */
-/* Miniupnp project : http://miniupnp.free.fr/
- * Author : Thomas Bernard
- * Copyright (c) 2005-2014 Thomas Bernard
- * This software is subject to the conditions detailed in the
- * LICENCE file provided within this distribution */
-#ifndef UPNPCOMMANDS_H_INCLUDED
-#define UPNPCOMMANDS_H_INCLUDED
-
-#include "upnpreplyparse.h"
-#include "portlistingparse.h"
-#include "declspec.h"
-#include "miniupnpctypes.h"
-
-/* MiniUPnPc return codes : */
-#define UPNPCOMMAND_SUCCESS (0)
-#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
-#define UPNPCOMMAND_INVALID_ARGS (-2)
-#define UPNPCOMMAND_HTTP_ERROR (-3)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-LIBSPEC UNSIGNED_INTEGER
-UPNP_GetTotalBytesSent(const char * controlURL,
- const char * servicetype);
-
-LIBSPEC UNSIGNED_INTEGER
-UPNP_GetTotalBytesReceived(const char * controlURL,
- const char * servicetype);
-
-LIBSPEC UNSIGNED_INTEGER
-UPNP_GetTotalPacketsSent(const char * controlURL,
- const char * servicetype);
-
-LIBSPEC UNSIGNED_INTEGER
-UPNP_GetTotalPacketsReceived(const char * controlURL,
- const char * servicetype);
-
-/* UPNP_GetStatusInfo()
- * status and lastconnerror are 64 byte buffers
- * Return values :
- * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
- * or a UPnP Error code */
-LIBSPEC int
-UPNP_GetStatusInfo(const char * controlURL,
- const char * servicetype,
- char * status,
- unsigned int * uptime,
- char * lastconnerror);
-
-/* UPNP_GetConnectionTypeInfo()
- * argument connectionType is a 64 character buffer
- * Return Values :
- * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
- * or a UPnP Error code */
-LIBSPEC int
-UPNP_GetConnectionTypeInfo(const char * controlURL,
- const char * servicetype,
- char * connectionType);
-
-/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
- * if the third arg is not null the value is copied to it.
- * at least 16 bytes must be available
- *
- * Return values :
- * 0 : SUCCESS
- * NON ZERO : ERROR Either an UPnP error code or an unknown error.
- *
- * possible UPnP Errors :
- * 402 Invalid Args - See UPnP Device Architecture section on Control.
- * 501 Action Failed - See UPnP Device Architecture section on Control. */
-LIBSPEC int
-UPNP_GetExternalIPAddress(const char * controlURL,
- const char * servicetype,
- char * extIpAdd);
-
-/* UPNP_GetLinkLayerMaxBitRates()
- * call WANCommonInterfaceConfig:1#GetCommonLinkProperties
- *
- * return values :
- * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
- * or a UPnP Error Code. */
-LIBSPEC int
-UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
- const char* servicetype,
- unsigned int * bitrateDown,
- unsigned int * bitrateUp);
-
-/* UPNP_AddPortMapping()
- * if desc is NULL, it will be defaulted to "libminiupnpc"
- * remoteHost is usually NULL because IGD don't support it.
- *
- * Return values :
- * 0 : SUCCESS
- * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
- *
- * List of possible UPnP errors for AddPortMapping :
- * errorCode errorDescription (short) - Description (long)
- * 402 Invalid Args - See UPnP Device Architecture section on Control.
- * 501 Action Failed - See UPnP Device Architecture section on Control.
- * 606 Action not authorized - The action requested REQUIRES authorization and
- * the sender was not authorized.
- * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
- * wild-carded
- * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
- * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
- * with a mapping assigned previously to another client
- * 724 SamePortValuesRequired - Internal and External port values
- * must be the same
- * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
- * permanent lease times on port mappings
- * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
- * and cannot be a specific IP address or DNS name
- * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
- * cannot be a specific port value
- * 728 NoPortMapsAvailable - There are not enough free ports available to
- * complete port mapping.
- * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
- * due to conflict with other mechanisms.
- * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
- */
-LIBSPEC int
-UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
- const char * extPort,
- const char * inPort,
- const char * inClient,
- const char * desc,
- const char * proto,
- const char * remoteHost,
- const char * leaseDuration);
-
-/* UPNP_DeletePortMapping()
- * Use same argument values as what was used for AddPortMapping().
- * remoteHost is usually NULL because IGD don't support it.
- * Return Values :
- * 0 : SUCCESS
- * NON ZERO : error. Either an UPnP error code or an undefined error.
- *
- * List of possible UPnP errors for DeletePortMapping :
- * 402 Invalid Args - See UPnP Device Architecture section on Control.
- * 606 Action not authorized - The action requested REQUIRES authorization
- * and the sender was not authorized.
- * 714 NoSuchEntryInArray - The specified value does not exist in the array */
-LIBSPEC int
-UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
- const char * extPort, const char * proto,
- const char * remoteHost);
-
-/* UPNP_GetPortMappingNumberOfEntries()
- * not supported by all routers */
-LIBSPEC int
-UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
- const char* servicetype,
- unsigned int * num);
-
-/* UPNP_GetSpecificPortMappingEntry()
- * retrieves an existing port mapping
- * params :
- * in extPort
- * in proto
- * in remoteHost
- * out intClient (16 bytes)
- * out intPort (6 bytes)
- * out desc (80 bytes)
- * out enabled (4 bytes)
- * out leaseDuration (16 bytes)
- *
- * return value :
- * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
- * or a UPnP Error Code.
- *
- * List of possible UPnP errors for _GetSpecificPortMappingEntry :
- * 402 Invalid Args - See UPnP Device Architecture section on Control.
- * 501 Action Failed - See UPnP Device Architecture section on Control.
- * 606 Action not authorized - The action requested REQUIRES authorization
- * and the sender was not authorized.
- * 714 NoSuchEntryInArray - The specified value does not exist in the array.
- */
-LIBSPEC int
-UPNP_GetSpecificPortMappingEntry(const char * controlURL,
- const char * servicetype,
- const char * extPort,
- const char * proto,
- const char * remoteHost,
- char * intClient,
- char * intPort,
- char * desc,
- char * enabled,
- char * leaseDuration);
-
-/* UPNP_GetGenericPortMappingEntry()
- * params :
- * in index
- * out extPort (6 bytes)
- * out intClient (16 bytes)
- * out intPort (6 bytes)
- * out protocol (4 bytes)
- * out desc (80 bytes)
- * out enabled (4 bytes)
- * out rHost (64 bytes)
- * out duration (16 bytes)
- *
- * return value :
- * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
- * or a UPnP Error Code.
- *
- * Possible UPNP Error codes :
- * 402 Invalid Args - See UPnP Device Architecture section on Control.
- * 606 Action not authorized - The action requested REQUIRES authorization
- * and the sender was not authorized.
- * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
- */
-LIBSPEC int
-UPNP_GetGenericPortMappingEntry(const char * controlURL,
- const char * servicetype,
- const char * index,
- char * extPort,
- char * intClient,
- char * intPort,
- char * protocol,
- char * desc,
- char * enabled,
- char * rHost,
- char * duration);
-
-/* UPNP_GetListOfPortMappings() Available in IGD v2
- *
- *
- * Possible UPNP Error codes :
- * 606 Action not Authorized
- * 730 PortMappingNotFound - no port mapping is found in the specified range.
- * 733 InconsistantParameters - NewStartPort and NewEndPort values are not
- * consistent.
- */
-LIBSPEC int
-UPNP_GetListOfPortMappings(const char * controlURL,
- const char * servicetype,
- const char * startPort,
- const char * endPort,
- const char * protocol,
- const char * numberOfPorts,
- struct PortMappingParserData * data);
-
-/* IGD:2, functions for service WANIPv6FirewallControl:1 */
-LIBSPEC int
-UPNP_GetFirewallStatus(const char * controlURL,
- const char * servicetype,
- int * firewallEnabled,
- int * inboundPinholeAllowed);
-
-LIBSPEC int
-UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
- const char * remoteHost,
- const char * remotePort,
- const char * intClient,
- const char * intPort,
- const char * proto,
- int * opTimeout);
-
-LIBSPEC int
-UPNP_AddPinhole(const char * controlURL, const char * servicetype,
- const char * remoteHost,
- const char * remotePort,
- const char * intClient,
- const char * intPort,
- const char * proto,
- const char * leaseTime,
- char * uniqueID);
-
-LIBSPEC int
-UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
- const char * uniqueID,
- const char * leaseTime);
-
-LIBSPEC int
-UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
-
-LIBSPEC int
-UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
- const char * uniqueID, int * isWorking);
-
-LIBSPEC int
-UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
- const char * uniqueID, int * packets);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-
+++ /dev/null
-/* $Id: upnpreplyparse.c,v 1.15 2013/06/06 21:36:40 nanard Exp $ */
-/* MiniUPnP project
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * (c) 2006-2013 Thomas Bernard
- * This software is subject to the conditions detailed
- * in the LICENCE file provided within the distribution */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "upnpreplyparse.h"
-#include "minixml.h"
-
-static void
-NameValueParserStartElt(void * d, const char * name, int l)
-{
- struct NameValueParserData * data = (struct NameValueParserData *)d;
- data->topelt = 1;
- if(l>63)
- l = 63;
- memcpy(data->curelt, name, l);
- data->curelt[l] = '\0';
- data->cdata = NULL;
- data->cdatalen = 0;
-}
-
-static void
-NameValueParserEndElt(void * d, const char * name, int l)
-{
- struct NameValueParserData * data = (struct NameValueParserData *)d;
- struct NameValue * nv;
- (void)name;
- (void)l;
- if(!data->topelt)
- return;
- if(strcmp(data->curelt, "NewPortListing") != 0)
- {
- int l;
- /* standard case. Limited to n chars strings */
- l = data->cdatalen;
- nv = malloc(sizeof(struct NameValue));
- if(l>=(int)sizeof(nv->value))
- l = sizeof(nv->value) - 1;
- strncpy(nv->name, data->curelt, 64);
- nv->name[63] = '\0';
- if(data->cdata != NULL)
- {
- memcpy(nv->value, data->cdata, l);
- nv->value[l] = '\0';
- }
- else
- {
- nv->value[0] = '\0';
- }
- LIST_INSERT_HEAD( &(data->head), nv, entries);
- }
- data->cdata = NULL;
- data->cdatalen = 0;
- data->topelt = 0;
-}
-
-static void
-NameValueParserGetData(void * d, const char * datas, int l)
-{
- struct NameValueParserData * data = (struct NameValueParserData *)d;
- if(strcmp(data->curelt, "NewPortListing") == 0)
- {
- /* specific case for NewPortListing which is a XML Document */
- data->portListing = malloc(l + 1);
- if(!data->portListing)
- {
- /* malloc error */
- return;
- }
- memcpy(data->portListing, datas, l);
- data->portListing[l] = '\0';
- data->portListingLength = l;
- }
- else
- {
- /* standard case. */
- data->cdata = datas;
- data->cdatalen = l;
- }
-}
-
-void
-ParseNameValue(const char * buffer, int bufsize,
- struct NameValueParserData * data)
-{
- struct xmlparser parser;
- LIST_INIT(&(data->head));
- data->portListing = NULL;
- data->portListingLength = 0;
- /* init xmlparser object */
- parser.xmlstart = buffer;
- parser.xmlsize = bufsize;
- parser.data = data;
- parser.starteltfunc = NameValueParserStartElt;
- parser.endeltfunc = NameValueParserEndElt;
- parser.datafunc = NameValueParserGetData;
- parser.attfunc = 0;
- parsexml(&parser);
-}
-
-void
-ClearNameValueList(struct NameValueParserData * pdata)
-{
- struct NameValue * nv;
- if(pdata->portListing)
- {
- free(pdata->portListing);
- pdata->portListing = NULL;
- pdata->portListingLength = 0;
- }
- while((nv = pdata->head.lh_first) != NULL)
- {
- LIST_REMOVE(nv, entries);
- free(nv);
- }
-}
-
-char *
-GetValueFromNameValueList(struct NameValueParserData * pdata,
- const char * Name)
-{
- struct NameValue * nv;
- char * p = NULL;
- for(nv = pdata->head.lh_first;
- (nv != NULL) && (p == NULL);
- nv = nv->entries.le_next)
- {
- if(strcmp(nv->name, Name) == 0)
- p = nv->value;
- }
- return p;
-}
-
-#if 0
-/* useless now that minixml ignores namespaces by itself */
-char *
-GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
- const char * Name)
-{
- struct NameValue * nv;
- char * p = NULL;
- char * pname;
- for(nv = pdata->head.lh_first;
- (nv != NULL) && (p == NULL);
- nv = nv->entries.le_next)
- {
- pname = strrchr(nv->name, ':');
- if(pname)
- pname++;
- else
- pname = nv->name;
- if(strcmp(pname, Name)==0)
- p = nv->value;
- }
- return p;
-}
-#endif
-
-/* debug all-in-one function
- * do parsing then display to stdout */
-#ifdef DEBUG
-void
-DisplayNameValueList(char * buffer, int bufsize)
-{
- struct NameValueParserData pdata;
- struct NameValue * nv;
- ParseNameValue(buffer, bufsize, &pdata);
- for(nv = pdata.head.lh_first;
- nv != NULL;
- nv = nv->entries.le_next)
- {
- printf("%s = %s\n", nv->name, nv->value);
- }
- ClearNameValueList(&pdata);
-}
-#endif
-
+++ /dev/null
-/* $Id: upnpreplyparse.h,v 1.17 2013/06/06 21:36:40 nanard Exp $ */
-/* MiniUPnP project
- * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * (c) 2006-2013 Thomas Bernard
- * This software is subject to the conditions detailed
- * in the LICENCE file provided within the distribution */
-
-#ifndef UPNPREPLYPARSE_H_INCLUDED
-#define UPNPREPLYPARSE_H_INCLUDED
-
-#if defined(NO_SYS_QUEUE_H) || defined(_WIN32) || defined(__HAIKU__)
-#include "bsdqueue.h"
-#else
-#include <sys/queue.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct NameValue {
- LIST_ENTRY(NameValue) entries;
- char name[64];
- char value[128];
-};
-
-struct NameValueParserData {
- LIST_HEAD(listhead, NameValue) head;
- char curelt[64];
- char * portListing;
- int portListingLength;
- int topelt;
- const char * cdata;
- int cdatalen;
-};
-
-/* ParseNameValue() */
-void
-ParseNameValue(const char * buffer, int bufsize,
- struct NameValueParserData * data);
-
-/* ClearNameValueList() */
-void
-ClearNameValueList(struct NameValueParserData * pdata);
-
-/* GetValueFromNameValueList() */
-char *
-GetValueFromNameValueList(struct NameValueParserData * pdata,
- const char * Name);
-
-#if 0
-/* GetValueFromNameValueListIgnoreNS() */
-char *
-GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
- const char * Name);
-#endif
-
-/* DisplayNameValueList() */
-#ifdef DEBUG
-void
-DisplayNameValueList(char * buffer, int bufsize);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
-