Compare commits

...

39 Commits

Author SHA1 Message Date
Marcelo Roberto Jimenez
bc335f5df7 Bug tracker #124 Build fails with --enable-debug
Build environment
Fedora 21
X86-64
gcc 4.9.2

how to repeat
$ ./configure --enable debug
$ make
libtool: compile: gcc -dhave_config_h -i. -i.. -i../upnp/inc -i./inc
-i../threadutil/inc
-i../ixml/inc -i./src/inc -pthread -g -o2 -wall -mt
src/api/libupnp_la-upnpstring.lo
-md -mp -mf src/api/.deps/libupnp_la-upnpstring.tpo -c
src/api/upnpstring.c
-fpic -dpic -o src/api .libs/libupnp_la-upnpstring.o
src/api/UpnpString.c:47:16:
error: expected identifier or '(' before 'extension'
extern char *strndup(const char *string, size_t __n);
^
Makefile:1016: recipe for target 'src/api/libupnp_la-UpnpString.lo'
failed

Reason for failure
Build enables -O2 optimization flags which causes the inclusion of a
macro implementation of strndup from include/bits/string2.h.

Workarounds
Disable optimization when configuring or making:
$ configure CFLAGS='-g -pthread -O0' --enable-debug
$ make
or
$ configure --enable-debug
$ make CFLAGS='-g -pthread -O0' Define NO_STRING_INLINES
$ export CFLAGS="-DNO_STRING_INLINES -O2"
$ ./configure --enagble-debug
$ make

Fix
* Don't declare strndup in src/api/UpnpString.c if it exists
2015-02-04 22:34:08 -02:00
Marcelo Roberto Jimenez
cf31814e55 Looks like I did something wrong two commits ago 2015-02-04 22:25:15 -02:00
Marcelo Roberto Jimenez
3b0fd070fd Update .gitignore 2015-02-04 22:18:23 -02:00
Marcelo Roberto Jimenez
9f69fb1b80 _LARGEFILE_SOURCE definition has changed in the last autotools 2015-02-01 22:31:30 -02:00
Jean-Francois Dockes
c124ae6507 Fix out-of-tree compilation: missing include directive and build
subdirectory

Out-of-tree builds seem to be currently broken, because ixml and
threadutil files need an include path to include UpnpGlobal.h, and
configure tries to copy files into a directory which it does not create.
The patch fixes both issues.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2015-02-01 22:27:39 -02:00
Nick Leverton
51a01cdba1 Adjust NUM_MEDIA_TYPES to allow for the addition of css in commit 72460df7. 2014-10-02 20:34:58 +01:00
Marcelo Jimenez
6905714a7e Update the THANKS file
Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-26 13:59:38 -03:00
Dirk
72460df747 #121 Support css files in webserver
Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-26 13:49:43 -03:00
Philipp Matthias Hahn
a596abfbe3 Add unit test for parse_uri()
Tested through the public function UpnpResolveURL2().

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:59:04 -03:00
Philipp Matthias Hahn
fbbb24f406 Fix remove_dots()
Rewrite to handle all normal and abnormal examples mentioned in RFC 3986
section 5.4.
The previous implementation failed the following test cases:

'http://www.libupnp.org/path1/path1' | '#frag1' -> 'http://www.libupnp.org/path1/#frag1' != 'http://www.libupnp.org/path1/path1#frag1' (0)
'http://127.0.0.1:6544/getDeviceDesc' | 'CDS_Event' -> 'http://127.0.0.1:6544/CDS_EventDesc' != 'http://127.0.0.1:6544/CDS_Event' (0)
'http://localhost/b/c/d;p?q' | 'g' -> 'http://localhost/b/c/g;p' != 'http://localhost/b/c/g' (0)
'http://localhost/b/c/d;p?q' | 'g/' -> 'http://localhost/b/c/g/p' != 'http://localhost/b/c/g/' (0)
'http://localhost/b/c/d;p?q' | '?y' -> 'http://localhost/b/c/?yp' != 'http://localhost/b/c/d;p?y' (0)
'http://localhost/b/c/d;p?q' | '#s' -> 'http://localhost/b/c/#sp' != 'http://localhost/b/c/d;p?q#s' (0)
'http://localhost/b/c/d;p?q' | ';x' -> 'http://localhost/b/c/;xp' != 'http://localhost/b/c/;x' (0)
'http://localhost/b/c/d;p?q' | '.' -> 'http://localhost/b/c/.;p' != 'http://localhost/b/c/' (0)
'http://localhost/b/c/d;p?q' | './' -> 'http://localhost/b/c/p' != 'http://localhost/b/c/' (0)
'http://localhost/b/c/d;p?q' | '..' -> 'http://localhost/b/c/..p' != 'http://localhost/b/' (0)
'http://localhost/b/c/d;p?q' | '/./g' -> 'http://localhost/./g' != 'http://localhost/g' (0)
'http://localhost/b/c/d;p?q' | '/../g' -> 'http://localhost/../g' != 'http://localhost/g' (0)
'http://localhost/b/c/d;p?q' | 'g.' -> 'http://localhost/b/c/g.p' != 'http://localhost/b/c/g.' (0)
'http://localhost/b/c/d;p?q' | '.g' -> 'http://localhost/b/c/.gp' != 'http://localhost/b/c/.g' (0)

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:59:04 -03:00
Philipp Matthias Hahn
0508fb0d6e Fix resolve_rel_url()
This reworks commit 0edaf3361d, which
broke resolving relative url, where the relative URL is shorter than the
absolute URL:
    "http://127.0.0.1:6544/getDeviceDesc" + "CDS_Event"
    Wrong: "http://127.0.0.1:6544/CDS_EventDesc"
    Right: "http://127.0.0.1:6544/CDS_Event"

While reviewing that commit, improve code by:
1. Move the simple cases to the beginning of the function.
2. Keep track of the remaining target buffer size.
3. Fix URI concatenation with queries.
4. Fix URI concatenation with fragments.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:19:25 -03:00
Philipp Matthias Hahn
848d66e69d Fix broken strncat(..., strlen())
commit 0edaf3361d replaced several
malloc()+strcat() sequences with strncat() using strlen() on the
*source* string.
This is still vulnerable to overwrite the *target* buffer.

While reviewing this commit change the code to directly use snprintf()
for concatenating strings and check the length of the target buffer.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:19:25 -03:00
Philipp Matthias Hahn
04fb684323 Directly use strdup()
commit 0edaf3361d replaced several
malloc()+strcpy() sequences with memset()+strncpy() using strlen().
This doesn't improve security and introduced a bug URI handling.

While reviewing this commit change the code to directly use strdup()
instead of re-implementing it multiple times, as shortens the code and
thus improves readability.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:19:25 -03:00
Fabrice Fontaine
faaef39a3c Fix getaddrinfo() loop
Commit b116d10f did the following change:
    Use switch, int and sa_family_t with AF_INET in uri.c.

This breaks when getaddrinfo() only returns a single record, as in that
case the "break" only exits the switch statement and the loop-step
"res=res->ai_next" is still executed. After that "res == NULL" is
wrongly interpreted as not having found an AF_INET or AF_INET6 address.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-09-22 11:19:25 -03:00
Peng
a1193f385e rewrite soap_device.c
1) separate HTTP handling from SOAP handling
2) remove repeated validity check, each check is performed exactly once
3) fix HTTP status code per UPnP spec, SOAP spec and RFC 2774

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2014-01-17 10:00:00 -02:00
Marcelo Roberto Jimenez
c20f2bd3a1 Homekeeping for the next release. 2013-11-15 14:16:13 -02:00
Marcelo Roberto Jimenez
3fc0f9ad1d Adjust the library numbers for release. 2013-11-15 14:10:05 -02:00
Marcelo Roberto Jimenez
2211cc14e6 Access violation due to changed usage of pthreads-win32
SF Bug Tracker #119, Creator: Klaus Fischer

Dear libupnp developers,

I have experienced a crash (access violation) when using libupnp on
Windows. The crash is actually located in pthreads-win32 and happens
when repeatedly de-/initializing libupnp on Win32 in the same process
and both libupnp and pthreads-win32 are compiled as static libraries.

So I'm doing this:
- UpnpInit()
- UpnpFinish()
- UpnpInit() <- Crash

I am already in touch with Ross Johnson on the pthreads-win32 mailing
list regarding this issue:

http://sourceware.org/ml/pthreads-win32/2013/msg00020.html

He told me the problem is that the functions
pthread_win32_process_attach/detach_np() should no longer be called
directly, but are invoked automatically now since version 2.9.0 of
pthreads-win32, which has been released approx. 1.5 years ago. Please
refer to above link for in-depth information.

So for proper using of latest pthreads-win32 library, those function
calls should vanish inside libupnp. Could you consider adapting libupnp
in that way? I would really like to use both libraries out-of-the-box
without local modifications, and this issue prevents that.

Best regards,
Klaus
2013-11-14 15:06:31 -02:00
Peng
bf60dc06e0 Fix several minor bugs in soap_device.c
1) remove redundant free
2) avoid user-provided ErrStr being overwritten by the default one
3) eliminated memory leak possiblity in handle_query_variable

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2013-11-08 16:03:09 -02:00
Peng
908785fba7 Fix return value check of parse_uri
Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2013-11-08 11:07:08 -02:00
Yoichi Nakayama
1a28e8ff51 tv_ctrlpt crashes after detecting a later version of tvcontrol service
SF Bug Tracker #118, Creator: T.Iwamoto

	From:  gon3456@users.sf.net
	Steps to reproduce:
	1. Extracts and build libupnp-1.6.18
	$ tar -xjf /path/to/archive/libupnp-1.6.18.tar.bz2
	$ cd libupnp-1.6.18
	$ ./configure
	$ make
	2. Applies the attached patch and remake.
	$ patch -p1 < /path/to/patch/libupnp-1.6.18.patch
	$ make
	3. Run tv_device.
	$ cd upnp/sample
	$ ./tv_device
	4. Run tv_ctrlpt; the tv_ctrlpt crashes soon.
	$ ./tv_ctrlpt
	Segmentation fault (core dumped)
	This is an issue report about the sample program of control point.
	The tv_ctrlpt crashes after detecting a tvdevice that contains tvcontrol:2 or higher version of tvcontrol service.
	tv_ctrlpt should detect correctly such devices due to forward compatibility of control points with device.
	For more information about the compatibility, please refer the following document:
	DLNA Architectures and Protocols Part 1 2011 December - 7.3.2.1.3 (GUN:GZJXU)
	The attached patch changes the sample programs as below:
	- device: changes version of tvcontrol service from 1 to 2. This change may occur in the future.
	- cp: nothing changed: cp knows version 1 of tvcontrol service only.
	I know many vendors implements their control points based on the tv_ctrlpt, so I hope to fix this issue ASAP.

	==

	From: Yoichi NAKAYAMA
	SEGV is caused by strcpy with NULL argument.
	Attached patch will avoid SEGV in strcpy, but there may be other inconsistencies.

	> I know many vendors implements their control points based on the tv_ctrlpt,

	I don't think so. I think tv_ctrlpt is just a sample to be used with tv_device.

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
2013-10-29 17:55:23 -02:00
Pino Toscano
89ad5e6779 Fix compilation on GNU/Hurd 2013-10-28 14:01:11 -02:00
Peng
062ac0c926 Fix return value of http_RecvPostMessage and update httpparser.c's comments 2013-10-28 11:35:23 -02:00
Peng
06aa3b17c4 Fix return value of process_request and related subroutines
1) Only HTTP_XXX should be return
2) Make default return value work for process_request
2013-10-28 11:34:58 -02:00
Peng
9f444a680e Fix Content-Range generation bug 2013-10-28 11:34:52 -02:00
zexian chen
8e3a71905b Fix memory leaks when when calling ThreadPoolAdd() or ThreadPoolAddPersistent()
Hi,

I had found some bugs about memory leak on libupnp-1.6.18.

It may lead to memory leak  when calling ThreadPoolAdd() or
ThreadPoolAddPersistent() which does not return 0.

See the attachment for patch.
2013-09-10 17:27:07 -03:00
Peng
72c29ef1f6 Fix return value of config_description_doc.
UPNP_E_XXX should not be used instead of IXML_XXX
2013-09-04 09:57:33 -03:00
Peng
f6a3102b48 Remove faulty free in GetDescDocumentAndURL.
temp_str, which points to part of description, should not be freed.
2013-09-04 09:57:32 -03:00
Peng
9a9c4e829e Avoid malicious client exploit to exhaust the device's memory
Suppose the UPnP device is listening on 192.168.1.102:49152. Use the following to send
garbage bytes to the device:
while true; do echo "\""; done | netcat 192.168.1.102 49152

The device just keeps receiving these bytes and its memory usage keeps growing.
Malicious client may exploit it to exhaust the device's memory.

The attached patch eliminates this possibility.
2013-09-02 14:56:11 -03:00
Peng
7a571f513e Scanner problems
1) restore the scanner's original cursor position in case of
   insufficient input;
2) free the memories allocated for a new header in case of a failure.
2013-09-02 14:38:04 -03:00
Peng
f10730f616 Patch to fix behaviou when char is signed
it seems to me that there is still something wrong:

	1)  the new is_qdtext_char() is incorrect.
	There is a trap if char is implemented as signed char.
	Suppose that c is '\xFF', it will be -1 when converted to an int.
	By definition, c should be qdtext:
	qdtext = <any TEXT except <">>
	TEXT = <any OCTET except CTLs, but including LWS>
	OCTET = <any 8-bit sequence of data>

	2) the character after '\\' could be either part of a quoted-pair
	(together with '\\'), or a normal qdtext, since '\\' itself can
	be treated as a qdtext. This is equivalent to saying that the
	character after '\\' in a quoted string could be ANY octet.

	A patch based on the above two observations is attached.

	Peng
2013-08-16 14:16:47 -03:00
Marcelo Roberto Jimenez
c70f5ce323 Enforce RFC 2616 and accept "0" after a backslash for quoted-strings.
Reported by Peng <howtofly(at)gmail.com>
2013-08-14 09:51:41 -03:00
Peng
a3c540bc9b scanner_get_token: robustness improvement
Patch to make scanner_get_token more robust (avoid over-reading).
2013-08-14 09:22:43 -03:00
Robert Buckley
7b1aa4c9e3 SF ticket #53 Action Error Response not returned
In soap_ctrlpt.c, in function get_response_value:

upnp_error_code is checked to see if it is less than 400 because that
would indicate a SOAP error code.

However it should be checked to see if it is greater than 400.
2013-07-30 17:07:30 -03:00
Zheng Peng
907c7c2621 SF ticket #116 UpnpRemoveVirtualDir wrong linked list operation
What if pVirtualDirList has two nodes and what we want to delete is the
first one. Patch attached.
2013-07-30 16:02:14 -03:00
Sebastian Brandt
25c27b8af7 Fix for crash in webserver.c by Sebastian Brandt
Dear libupnp-devels,
when POST'ing to the simple web server in libupnp, the application crashes.
This is caused by a missing "..." argument in webserver.c:1533.
Seems it has been there for a long time ... 1.6.9 and 1.6.18 have it.

webserver.c:1533 calls http_MakeMessage
/* Send response. */
http_MakeMessage(&headers, 1, 1,
	 "RTLSXcCc",
	 ret, "text/html", X_USER_AGENT);
The format parameter RTLSXcCc needs four arguments -
R - response code - ret,
T- content type - text/html,
L - struct SendInstruction * - NOT PRESENT
X - user agent - X_USER_AGENT

This results in a crash.

Changing to
http_MakeMessage(&headers, 1, 1,
	 "RTLSXcCc",
	 ret, "text/html", &RespInstr, X_USER_AGENT);
solves the situation.

Yours,
Sebastian Brandt
2013-07-30 11:46:46 -03:00
Marcelo Roberto Jimenez
dcbbc30f5c Fix for portability issue with GNU make extension 'sort'
upnp/sample/Makefile.am:67: warning: sort \
upnp/sample/Makefile.am:67:                     $(tv_ctrlpt_SOURCES: non-POSIX variable name
upnp/sample/Makefile.am:67: (probably a GNU make extension)

Reference:
http://debbugs.gnu.org/cgi/bugreport.cgi?bug=13771#8
2013-07-30 11:26:09 -03:00
Marcelo Roberto Jimenez
3509991872 Fix for warning issue in libtool
Reference:
http://lists.gnu.org/archive/html/automake/2012-05/msg00014.html

Re: automake 1.12 and AM_PROG_AR

From:	 Nick Bowler
Subject:	 Re: automake 1.12 and AM_PROG_AR
Date:	 Thu, 10 May 2012 17:16:04 -0400
User-agent:	 Mutt/1.5.21 (2010-09-15)
On 2012-05-10 12:52 +0200, Simon Josefsson wrote:
> Hi,
>
> I have received patches similar to the one below for several of my
> projects already, and I'm beginning to think there may be something that
> could be improved in automake.  Why does automake 1.12 need something
> that 1.11 doesn't even recognize?  Perhaps a smoother way to introduce
> this behaviour is to let 1.12 automatically enable it?
>
> Alternatively, is there some other way to address the warnings that is
> better?  Does it indicate a real problem in my projects?
>
> http://lists.gnu.org/archive/html/help-libidn/2012-05/msg00000.html
> http://lists.gnu.org/archive/html/help-libidn/2012-05/txtqAGOGgtPqj.txt

FYI, since the patch is so short it would have been helpful to include
it inline in your email.  I've taken the liberty of reproducing it
below:

> --- libidn-1.24.orig/configure.ac
> +++ libidn-1.24/configure.ac
> @@ -34,6 +34,10 @@ AC_SUBST(LT_CURRENT, 17)
>  AC_SUBST(LT_REVISION, 7)
>  AC_SUBST(LT_AGE, 6)
>
> +# automake 1.12 seems to require this, but automake 1.11 doesn't recognize it
> +m4_pattern_allow([AM_PROG_AR])
> +AM_PROG_AR
> +
>  AC_PROG_CC
>  gl_EARLY
>  lgl_EARLY

First off, this patch seems wrong at a first glance by its use of
m4_pattern_allow: this is saying that "It's ok for AM_PROG_AR to appear
verbatim in the configure script".  This suggests to me that the patch
didn't actually get tested (well) on Automake 1.11, since there will
almost certainly be an error about AM_PROG_AR at configure time.

This one should actually work properly on both old and new Automake
(untested):

  m4_ifdef([AM_PROG_AR], [AM_PROG_AR])

That being said, the new warning is enabled by the -Wextra-portability
option that was added to Automake-1.12.  I'm by no means an export on
this issue, but I think it relates to new support for building with the
Microsoft compiler: in other words, I think that ignoring this warning
will not cause regressions in your package but, instead, your package
will simply not be able to make use of the new feature.

Regardless, I believe that for most packages the new warning is actually
caused by Libtool, and therefore the correct place to add the call to
AM_PROG_AR is in libtool itself, rather than ad-hoc patching of each
package individually.  You can silence the warnings (but not fix them)
by adding -Wno-extra-portability to AM_INIT_AUTOMAKE.

Cheers,
--
Nick Bowler, Elliptic Technologies (http://www.elliptictech.com/)
2013-07-30 10:39:30 -03:00
Marcelo Roberto Jimenez
6279b7fa5f Homekeeping for the next release. 2013-01-29 12:11:56 -02:00
32 changed files with 1217 additions and 847 deletions

10
.gitignore vendored
View File

@@ -73,6 +73,14 @@ GRTAGS
GSYMS
GTAGS
# QT-Creator files
Makefile.am.user
pupnp.config
pupnp.creator
pupnp.creator.user
pupnp.files
pupnp.includes
*.orig
*~
\#*#
@@ -109,4 +117,4 @@ docs/doxygen
/build/vc10/out.vc9.Win32/Debug
/build/vc10/out.vc10.Win32
/build/vc10/out.vc10.x64
/pthreads
/pthreads

278
ChangeLog
View File

@@ -1,3 +1,281 @@
*******************************************************************************
Version 1.6.20
*******************************************************************************
2015-02-04 Shaun Marko <semarko@users.sf.net>
Bug tracker #124 Build fails with --enable-debug
Build environment
Fedora 21
X86-64
* gcc 4.9.2
How to repeat
$ ./configure --enable debug
$ make
libtool: compile: gcc -DHAVE_CONFIG_H -I. -I.. -I../upnp/inc -I./inc -I../threadutil/inc
-I../ixml/inc -I./src/inc -pthread -g -O2 -Wall -MT src/api/libupnp_la-UpnpString.lo
-MD -MP -MF src/api/.deps/libupnp_la-UpnpString.Tpo -c src/api/UpnpString.c
-fPIC -DPIC -o src/api .libs/libupnp_la-UpnpString.o src/api/UpnpString.c:47:16:
error: expected identifier or '(' before 'extension'
extern char *strndup(const char *string, size_t __n);
^
Makefile:1016: recipe for target 'src/api/libupnp_la-UpnpString.lo' failed
Reason for failure
Build enables -O2 optimization flags which causes the inclusion of a
macro implementation of strndup from include/bits/string2.h.
Workarounds
Disable optimization when configuring or making:
$ configure CFLAGS='-g -pthread -O0' --enable-debug
$ make
or
$ configure --enable-debug
$ make CFLAGS='-g -pthread -O0' Define NO_STRING_INLINES
$ export CFLAGS="-DNO_STRING_INLINES -O2"
$ ./configure --enagble-debug
$ make
Fix
* Don't declare strndup in src/api/UpnpString.c if it exists
2015-02-01 Jean-Francois Dockes <medoc@users.sf.net>
Out-of-tree builds seem to be currently broken, because ixml and
threadutil files need an include path to include UpnpGlobal.h, and
configure tries to copy files into a directory which it does not create.
The patch fixes both issues.
2014-01-03 Peng <howtofly(at)gmail.com>
rewrite soap_device.c
1) separate HTTP handling from SOAP handling
2) remove repeated validity check, each check is performed exactly once
3) fix HTTP status code per UPnP spec, SOAP spec and RFC 2774
*******************************************************************************
Version 1.6.19
*******************************************************************************
2013-11-14 Marcelo Roberto Jimenez <mroberto(at)users.sourceforge.net>
SF Bug Tracker #119, Creator: Klaus Fischer
Access violation due to changed usage of pthreads-win32
Dear libupnp developers,
I have experienced a crash (access violation) when using libupnp on
Windows. The crash is actually located in pthreads-win32 and happens
when repeatedly de-/initializing libupnp on Win32 in the same process
and both libupnp and pthreads-win32 are compiled as static libraries.
So I'm doing this:
- UpnpInit()
- UpnpFinish()
- UpnpInit() <- Crash
I am already in touch with Ross Johnson on the pthreads-win32 mailing
list regarding this issue:
http://sourceware.org/ml/pthreads-win32/2013/msg00020.html
He told me the problem is that the functions
pthread_win32_process_attach/detach_np() should no longer be called
directly, but are invoked automatically now since version 2.9.0 of
pthreads-win32, which has been released approx. 1.5 years ago. Please
refer to above link for in-depth information.
So for proper using of latest pthreads-win32 library, those function
calls should vanish inside libupnp. Could you consider adapting libupnp
in that way? I would really like to use both libraries out-of-the-box
without local modifications, and this issue prevents that.
Best regards,
Klaus
2013-11-08 Peng <howtofly(at)gmail.com>
Fix several minor bugs in soap_device.c
1) remove redundant free
2) avoid user-provided ErrStr being overwritten by the default one
3) eliminated memory leak possiblity in handle_query_variable
2013-11-08 Peng <howtofly(at)gmail.com>
Fix return value check of parse_uri.
2012-06-19 Yoichi NAKAYAMA <yoichi.nakayama(at)gmail.com>
SF Bug Tracker #118, Creator: T.Iwamoto
tv_ctrlpt crashes after detecting a later version of tvcontrol service
From: gon3456@users.sf.net
Steps to reproduce:
1. Extracts and build libupnp-1.6.18
$ tar -xjf /path/to/archive/libupnp-1.6.18.tar.bz2
$ cd libupnp-1.6.18
$ ./configure
$ make
2. Applies the attached patch and remake.
$ patch -p1 < /path/to/patch/libupnp-1.6.18.patch
$ make
3. Run tv_device.
$ cd upnp/sample
$ ./tv_device
4. Run tv_ctrlpt; the tv_ctrlpt crashes soon.
$ ./tv_ctrlpt
Segmentation fault (core dumped)
This is an issue report about the sample program of control point.
The tv_ctrlpt crashes after detecting a tvdevice that contains tvcontrol:2 or higher version of tvcontrol service.
tv_ctrlpt should detect correctly such devices due to forward compatibility of control points with device.
For more information about the compatibility, please refer the following document:
DLNA Architectures and Protocols Part 1 2011 December - 7.3.2.1.3 (GUN:GZJXU)
The attached patch changes the sample programs as below:
- device: changes version of tvcontrol service from 1 to 2. This change may occur in the future.
- cp: nothing changed: cp knows version 1 of tvcontrol service only.
I know many vendors implements their control points based on the tv_ctrlpt, so I hope to fix this issue ASAP.
==
From: Yoichi NAKAYAMA
SEGV is caused by strcpy with NULL argument.
Attached patch will avoid SEGV in strcpy, but there may be other inconsistencies.
> I know many vendors implements their control points based on the tv_ctrlpt,
I don't think so. I think tv_ctrlpt is just a sample to be used with tv_device.
2013-10-28 Pino Toscano <pinotree(at)users.sourceforge.net>
Fix compilation on GNU/Hurd
2013-10-28 Peng <howtofly(at)gmail.com>
Fix return value of http_RecvPostMessage and update httpparser.c's comments
2013-10-17 Peng <howtofly(at)gmail.com>
Fix return value of process_request and related subroutines
1) Only HTTP_XXX should be return
2) Make default return value work for process_request
2013-10-15 Peng <howtofly(at)gmail.com>
Fix Content-Range generation bug
2013-09-10 zexian chen <chenzexian88(at)gmail.com>
Hi,
I had found some bugs about memory leak on libupnp-1.6.18.
It may lead to memory leak when calling ThreadPoolAdd() or
ThreadPoolAddPersistent() which does not return 0.
See the attachment for patch.
2013-09-03 Peng <howtofly(at)gmail.com>
Fix return value of config_description_doc.
UPNP_E_XXX should not be used instead of IXML_XXX
2013-09-03 Peng <howtofly(at)gmail.com>
Remove faulty free in GetDescDocumentAndURL.
temp_str, which points to part of description, should not be freed.
2013-09-02 Peng <howtofly(at)gmail.com>
Suppose the UPnP device is listening on 192.168.1.102:49152. Use the following to send
garbage bytes to the device:
while true; do echo "\""; done | netcat 192.168.1.102 49152
The device just keeps receiving these bytes and its memory usage keeps growing.
Malicious client may exploit it to exhaust the device's memory.
The attached patch eliminates this possibility.
2013-09-02 Peng <howtofly(at)gmail.com>
1) restore the scanner's original cursor position in case of
insufficient input;
2) free the memories allocated for a new header in case of a failure.
2013-08-13 Peng <howtofly(at)gmail.com>
Patch to fix behaviou when char is signed
it seems to me that there is still something wrong:
1) the new is_qdtext_char() is incorrect.
There is a trap if char is implemented as signed char.
Suppose that c is '\xFF', it will be -1 when converted to an int.
By definition, c should be qdtext:
qdtext = <any TEXT except <">>
TEXT = <any OCTET except CTLs, but including LWS>
OCTET = <any 8-bit sequence of data>
2) the character after '\\' could be either part of a quoted-pair
(together with '\\'), or a normal qdtext, since '\\' itself can
be treated as a qdtext. This is equivalent to saying that the
character after '\\' in a quoted string could be ANY octet.
A patch based on the above two observations is attached.
Peng
2013-08-13 Marcelo Roberto Jimenez <mroberto(at)users.sourceforge.net>
Enforce RFC 2616 and accept "0" after a backslash for quoted-strings.
Reported by Peng <howtofly(at)gmail.com>
2013-08-13 Peng <howtofly(at)gmail.com>
Patch to make scanner_get_token more robust (avoid over-reading).
2013-07-30 Zheng Peng <darkelf2010(at)users.sf.net>
SF ticket #116 UpnpRemoveVirtualDir wrong linked list operation
What if pVirtualDirList has two nodes and what we want to delete is the
first one. Patch attached.
2013-07-30 Sebastian Brandt <s.brandt(at)aixtrusion.de>
Dear libupnp-devels,
when POST'ing to the simple web server in libupnp, the application crashes.
This is caused by a missing "..." argument in webserver.c:1533.
Seems it has been there for a long time ... 1.6.9 and 1.6.18 have it.
webserver.c:1533 calls http_MakeMessage
/* Send response. */
http_MakeMessage(&headers, 1, 1,
"RTLSXcCc",
ret, "text/html", X_USER_AGENT);
The format parameter RTLSXcCc needs four arguments -
R - response code - ret,
T- content type - text/html,
L - struct SendInstruction * - NOT PRESENT
X - user agent - X_USER_AGENT
This results in a crash.
Changing to
http_MakeMessage(&headers, 1, 1,
"RTLSXcCc",
ret, "text/html", &RespInstr, X_USER_AGENT);
solves the situation.
Yours,
Sebastian Brandt
*******************************************************************************
Version 1.6.18
*******************************************************************************

View File

@@ -31,7 +31,7 @@ PROJECT_NAME = libUPnP
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = 1.6.18
PROJECT_NUMBER = 1.6.20
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.

10
THANKS
View File

@@ -23,6 +23,7 @@ exempt of errors.
- Craig Nelson
- David Blanchet
- David Maass
- Dirk (dirk_vdb)
- Emil Ljungdahl
- Erik Johansson
- Eric Tanguy
@@ -37,6 +38,7 @@ exempt of errors.
- Ingo Hofmann
- Ivan Romanov (ivanromanov)
- Jiri Zouhar
- Jean-Francois Dockes (medoc)
- John Dennis
- Jonathan Casiot (no_dice)
- Josh Carroll
@@ -54,10 +56,16 @@ exempt of errors.
- Oskar Liljeblad
- Michael (oxygenic)
- Paul Vixie
- Peng
- Peter Hartley
- Philipp Matthias Hahn
- Pino Toscano (pinotree)
- Rene Hexel
- Robert Buckley (rbuckley)
- Robert Gingher (robsbox)
- Ronan Menard
- Sebastian Brandt
- Shaun Marko (semarko)
- Siva Chandran
- Stefan Sommerfeld (zerocom)
- Stéphane Corthésy
@@ -68,4 +76,6 @@ exempt of errors.
- Tom (tomdev2)
- Yoichi Nakayama (yoichi)
- zephyrus (zephyrus00jp)
- zexian chen
- Zheng Peng (darkelf2010)

View File

@@ -105,7 +105,7 @@
#define PACKAGE_NAME "libupnp"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "libupnp 1.6.18"
#define PACKAGE_STRING "libupnp 1.6.20"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libupnp"
@@ -114,7 +114,7 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.6.18"
#define PACKAGE_VERSION "1.6.20"
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
@@ -172,13 +172,13 @@
#define UPNP_VERSION_MINOR 6
/* see upnpconfig.h */
#define UPNP_VERSION_PATCH 18
#define UPNP_VERSION_PATCH 20
/* see upnpconfig.h */
#define UPNP_VERSION_STRING "1.6.18"
#define UPNP_VERSION_STRING "1.6.20"
/* Version number of package */
#define VERSION "1.6.18"
#define VERSION "1.6.20"
/* File Offset size */
#define _FILE_OFFSET_BITS 64

View File

@@ -40,7 +40,7 @@
***************************************************************************/
/** The library version (string) e.g. "1.3.0" */
#define UPNP_VERSION_STRING "1.6.18"
#define UPNP_VERSION_STRING "1.6.20"
/** Major version of the library */
#define UPNP_VERSION_MAJOR 1
@@ -49,7 +49,7 @@
#define UPNP_VERSION_MINOR 6
/** Patch version of the library */
#define UPNP_VERSION_PATCH 18
#define UPNP_VERSION_PATCH 20
/** The library version (numeric) e.g. 10300 means version 1.3.0 */
#define UPNP_VERSION \

View File

@@ -9,7 +9,7 @@
AC_PREREQ(2.60)
AC_INIT([libupnp], [1.6.18], [mroberto@users.sourceforge.net])
AC_INIT([libupnp], [1.6.20], [mroberto@users.sourceforge.net])
dnl ############################################################################
dnl # *Independently* of the above libupnp package version, the libtool version
dnl # of the 3 libraries need to be updated whenever there is a change released:
@@ -328,9 +328,32 @@ dnl #AC_SUBST([LT_VERSION_THREADUTIL], [6:3:0])
dnl #AC_SUBST([LT_VERSION_UPNP], [9:2:3])
dnl #
dnl ############################################################################
dnl # Release 1.6.19:
dnl # "current:revision:age"
dnl #
dnl # - Code has changed in threadutil
dnl # revision: 3 -> 4
dnl # - Code has changed in upnp
dnl # revision: 2 -> 3
dnl #
dnl #AC_SUBST([LT_VERSION_IXML], [2:8:0])
dnl #AC_SUBST([LT_VERSION_THREADUTIL], [6:4:0])
dnl #AC_SUBST([LT_VERSION_UPNP], [9:3:3])
dnl #
dnl ############################################################################
dnl # Release 1.6.20:
dnl # "current:revision:age"
dnl #
dnl # -
dnl #
dnl #AC_SUBST([LT_VERSION_IXML], [::])
dnl #AC_SUBST([LT_VERSION_THREADUTIL], [::])
dnl #AC_SUBST([LT_VERSION_UPNP], [::])
dnl #
dnl ############################################################################
AC_SUBST([LT_VERSION_IXML], [2:8:0])
AC_SUBST([LT_VERSION_THREADUTIL], [6:3:0])
AC_SUBST([LT_VERSION_UPNP], [9:2:3])
AC_SUBST([LT_VERSION_THREADUTIL], [6:4:0])
AC_SUBST([LT_VERSION_UPNP], [9:3:3])
dnl ############################################################################
dnl # Repeating the algorithm to place it closer to the modificatin place:
dnl # - library code modified: revision++
@@ -508,6 +531,7 @@ AC_MSG_RESULT($docdir)
#
AC_PROG_CC
AM_PROG_CC_C_O
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
AC_PROG_LIBTOOL
AC_PROG_INSTALL
AC_PROG_MAKE_SET
@@ -701,6 +725,7 @@ AC_OUTPUT
# Files copied for windows compilation.
#
echo "configure: copying \"autoconfig.h\" to \"build/inc/autoconfig.h\""
test -d build/inc || mkdir -p build/inc
cp autoconfig.h build/inc/autoconfig.h
echo "configure: copying \"upnp/inc/upnpconfig.h\" to \"build/inc/upnpconfig.h\""
cp upnp/inc/upnpconfig.h build/inc/upnpconfig.h

View File

@@ -7,7 +7,8 @@
SUBDIRS = doc
AM_CPPFLAGS = -I$(srcdir)/inc -I$(srcdir)/src/inc
AM_CPPFLAGS = -I$(srcdir)/inc -I$(srcdir)/src/inc \
-I$(top_srcdir)/upnp/inc
AM_CFLAGS =
LDADD = libixml.la

View File

@@ -1,4 +1,4 @@
Version: 1.6.18
Version: 1.6.20
Summary: Universal Plug and Play (UPnP) SDK
Name: libupnp
Release: 1%{?dist}

View File

@@ -4,7 +4,8 @@
# (C) Copyright 2005 Remi Turboult <r3mi@users.sourceforge.net>
#
AM_CPPFLAGS = -I$(srcdir)/inc -I$(srcdir)/src/inc
AM_CPPFLAGS = -I$(srcdir)/inc -I$(srcdir)/src/inc \
-I$(top_srcdir)/upnp/inc
if ENABLE_DEBUG
AM_CPPFLAGS += -DDEBUG -DSTATS

View File

@@ -50,7 +50,7 @@ extern "C" {
#include <pthread.h>
#if defined(BSD)
#if defined(BSD) && !defined(__GNU__)
#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
#endif
@@ -200,10 +200,6 @@ typedef pthread_rwlockattr_t ithread_rwlockattr_t;
static UPNP_INLINE int ithread_initialize_library(void) {
int ret = 0;
#if defined(WIN32) && defined(PTW32_STATIC_LIB)
ret = !pthread_win32_process_attach_np();
#endif
return ret;
}
@@ -222,10 +218,6 @@ static UPNP_INLINE int ithread_initialize_library(void) {
static UPNP_INLINE int ithread_cleanup_library(void) {
int ret = 0;
#if defined(WIN32) && defined(PTW32_STATIC_LIB)
ret = !pthread_win32_process_detach_np();
#endif
return ret;
}

View File

@@ -96,10 +96,17 @@ static void *TimerThreadWorker(
/* If time has elapsed, schedule job. */
if (nextEvent && currentTime >= nextEventTime) {
if( nextEvent->persistent ) {
ThreadPoolAddPersistent( timer->tp, &nextEvent->job,
&tempId );
if (ThreadPoolAddPersistent( timer->tp, &nextEvent->job, &tempId ) != 0) {
if (nextEvent->job.arg != NULL && nextEvent->job.free_func != NULL) {
nextEvent->job.free_func(nextEvent->job.arg);
}
}
} else {
ThreadPoolAdd( timer->tp, &nextEvent->job, &tempId );
if (ThreadPoolAdd( timer->tp, &nextEvent->job, &tempId ) != 0) {
if (nextEvent->job.arg != NULL && nextEvent->job.free_func != NULL) {
nextEvent->job.free_func(nextEvent->job.arg);
}
}
}
ListDelNode( &timer->eventQ, head, 0 );
FreeTimerEvent( timer, nextEvent );

View File

@@ -154,9 +154,10 @@ libupnp_la_SOURCES += \
# check / distcheck tests
check_PROGRAMS = test_init
TESTS = test_init
check_PROGRAMS = test_init test_url
TESTS = test_init test_url
test_init_SOURCES = test/test_init.c
test_url_SOURCES = test/test_url.c
EXTRA_DIST = \

View File

@@ -65,9 +65,8 @@ tv_combo_SOURCES = \
if WITH_DOCUMENTATION
examplesdir = $(docdir)/examples
examples_DATA = \
$(sort \
$(tv_ctrlpt_SOURCES) \
$(tv_device_SOURCES))
$(tv_ctrlpt_SOURCES) \
$(tv_device_SOURCES)
endif
EXTRA_DIST = \

View File

@@ -741,6 +741,10 @@ void TvCtrlPointAddDevice(
deviceNode->device.AdvrTimeOut = expires;
for (service = 0; service < TV_SERVICE_SERVCOUNT;
service++) {
if (serviceId[service] == NULL) {
/* not found */
continue;
}
strcpy(deviceNode->device.TvService[service].
ServiceId, serviceId[service]);
strcpy(deviceNode->device.TvService[service].

View File

@@ -43,9 +43,7 @@
#endif /* WIN32 */
/* strndup() is a GNU extension. */
#if HAVE_STRNDUP && !defined(WIN32)
extern char *strndup(__const char *__string, size_t __n);
#else /* HAVE_STRNDUP && !defined(WIN32) */
#if !HAVE_STRNDUP || defined(WIN32)
static char *strndup(const char *__string, size_t __n)
{
size_t strsize = strnlen(__string, __n);

View File

@@ -1590,7 +1590,6 @@ static int GetDescDocumentAndURL(
}
if (strlen(temp_str) > (LINE_SIZE - 1)) {
ixmlDocument_free(*xmlDoc);
free(temp_str);
return UPNP_E_URL_TOO_BIG;
}
strncpy(aliasStr, temp_str, sizeof(aliasStr) - 1);
@@ -1981,7 +1980,9 @@ int UpnpSubscribeAsync(
TPJobInit(&job, (start_routine)UpnpThreadDistribution, Param);
TPJobSetFreeFunction(&job, (free_routine)free);
TPJobSetPriority(&job, MED_PRIORITY);
ThreadPoolAdd(&gSendThreadPool, &job, NULL);
if (ThreadPoolAdd(&gSendThreadPool, &job, NULL) != 0) {
free(Param);
}
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Exiting UpnpSubscribeAsync\n");
@@ -2167,7 +2168,9 @@ int UpnpUnSubscribeAsync(
TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param );
TPJobSetFreeFunction( &job, ( free_routine ) free );
TPJobSetPriority( &job, MED_PRIORITY );
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
if (ThreadPoolAdd( &gSendThreadPool, &job, NULL ) != 0) {
free(Param);
}
exit_function:
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__, "Exiting UpnpUnSubscribeAsync\n");
@@ -2293,7 +2296,9 @@ int UpnpRenewSubscriptionAsync(
TPJobInit( &job, ( start_routine ) UpnpThreadDistribution, Param );
TPJobSetFreeFunction( &job, ( free_routine ) free );
TPJobSetPriority( &job, MED_PRIORITY );
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
if (ThreadPoolAdd( &gSendThreadPool, &job, NULL ) != 0) {
free(Param);
}
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Exiting UpnpRenewSubscriptionAsync\n");
@@ -2765,7 +2770,9 @@ int UpnpSendActionAsync(
TPJobSetFreeFunction( &job, ( free_routine ) free );
TPJobSetPriority( &job, MED_PRIORITY );
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
if (ThreadPoolAdd( &gSendThreadPool, &job, NULL ) != 0) {
free(Param);
}
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Exiting UpnpSendActionAsync \n");
@@ -2885,7 +2892,9 @@ int UpnpSendActionExAsync(
TPJobSetFreeFunction( &job, ( free_routine ) free );
TPJobSetPriority( &job, MED_PRIORITY );
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
if (ThreadPoolAdd( &gSendThreadPool, &job, NULL ) != 0) {
free(Param);
}
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Exiting UpnpSendActionAsync\n");
@@ -2952,7 +2961,9 @@ int UpnpGetServiceVarStatusAsync(
TPJobSetPriority( &job, MED_PRIORITY );
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
if (ThreadPoolAdd( &gSendThreadPool, &job, NULL ) != 0) {
free(Param);
}
UpnpPrintf(UPNP_ALL, API, __FILE__, __LINE__,
"Exiting UpnpGetServiceVarStatusAsync\n");
@@ -4085,16 +4096,17 @@ int UpnpRemoveVirtualDir(const char *dirName)
return UPNP_E_INVALID_PARAM;
}
/* Handle the special case where the directory that we are */
/* removing is the first and only one in the list. */
if( ( pVirtualDirList->next == NULL ) &&
( strcmp( pVirtualDirList->dirName, dirName ) == 0 ) ) {
free( pVirtualDirList );
pVirtualDirList = NULL;
/* removing is the first in the list. */
if (strcmp( pVirtualDirList->dirName, dirName ) == 0)
{
pPrev = pVirtualDirList;
pVirtualDirList = pVirtualDirList->next;
free( pPrev );
return UPNP_E_SUCCESS;
}
pCur = pVirtualDirList;
pPrev = pCur;
pCur = pVirtualDirList->next;
pPrev = pVirtualDirList;
while( pCur != NULL ) {
if( strcmp( pCur->dirName, dirName ) == 0 ) {

View File

@@ -480,25 +480,20 @@ int genaInitNotify(
}
*reference_count = 0;
UDN_copy = (char *)malloc(strlen(UDN) + 1);
UDN_copy = strdup(UDN);
if (UDN_copy == NULL) {
line = __LINE__;
ret = UPNP_E_OUTOF_MEMORY;
goto ExitFunction;
}
servId_copy = (char *)malloc(strlen(servId) + 1);
servId_copy = strdup(servId);
if (servId_copy == NULL) {
line = __LINE__;
ret = UPNP_E_OUTOF_MEMORY;
goto ExitFunction;
}
memset(UDN_copy, 0, strlen(UDN) + 1);
strncpy(UDN_copy, UDN, strlen(UDN));
memset(servId_copy, 0, strlen(servId) + 1);
strncpy(servId_copy, servId, strlen(servId));
HandleLock();
if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
@@ -639,25 +634,20 @@ int genaInitNotifyExt(
}
*reference_count = 0;
UDN_copy = (char *)malloc(strlen(UDN) + 1);
UDN_copy = strdup(UDN);
if (UDN_copy == NULL) {
line = __LINE__;
ret = UPNP_E_OUTOF_MEMORY;
goto ExitFunction;
}
servId_copy = (char *)malloc(strlen(servId) + 1);
servId_copy = strdup(servId);
if( servId_copy == NULL ) {
line = __LINE__;
ret = UPNP_E_OUTOF_MEMORY;
goto ExitFunction;
}
memset(UDN_copy, 0, strlen(UDN) + 1);
strncpy(UDN_copy, UDN, strlen(UDN));
memset(servId_copy, 0, strlen(servId) + 1);
strncpy(servId_copy, servId, strlen(servId));
HandleLock();
if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
@@ -798,25 +788,20 @@ int genaNotifyAllExt(
}
*reference_count = 0;
UDN_copy = (char *)malloc(strlen(UDN) + 1);
UDN_copy = strdup(UDN);
if (UDN_copy == NULL) {
line = __LINE__;
ret = UPNP_E_OUTOF_MEMORY;
goto ExitFunction;
}
servId_copy = (char *)malloc(strlen(servId) + 1);
servId_copy = strdup(servId);
if( servId_copy == NULL ) {
line = __LINE__;
ret = UPNP_E_OUTOF_MEMORY;
goto ExitFunction;
}
memset(UDN_copy, 0, strlen(UDN) + 1);
strncpy(UDN_copy, UDN, strlen(UDN));
memset(servId_copy, 0, strlen(servId) + 1);
strncpy(servId_copy, servId, strlen(servId));
propertySet = ixmlPrintNode((IXML_Node *)PropSet);
if (propertySet == NULL) {
line = __LINE__;
@@ -944,25 +929,20 @@ int genaNotifyAll(
}
*reference_count = 0;
UDN_copy = (char *)malloc(strlen(UDN) + 1);
UDN_copy = strdup(UDN);
if (UDN_copy == NULL) {
line = __LINE__;
ret = UPNP_E_OUTOF_MEMORY;
goto ExitFunction;
}
servId_copy = (char *)malloc(strlen(servId) + 1);
servId_copy = strdup(servId);
if( servId_copy == NULL ) {
line = __LINE__;
ret = UPNP_E_OUTOF_MEMORY;
goto ExitFunction;
}
memset(UDN_copy, 0, strlen(UDN) + 1);
strncpy(UDN_copy, UDN, strlen(UDN));
memset(servId_copy, 0, strlen(servId) + 1);
strncpy(servId_copy, servId, strlen(servId));
ret = GeneratePropertySet(VarNames, VarValues, var_count, &propertySet);
if (ret != XML_SUCCESS) {
line = __LINE__;

View File

@@ -910,6 +910,7 @@ int StartMiniServer(
sock_close(miniSocket->ssdpReqSock4);
sock_close(miniSocket->ssdpReqSock6);
#endif /* INCLUDE_CLIENT_APIS */
free(miniSocket);
return UPNP_E_OUTOF_MEMORY;
}
/* Wait for miniserver to start. */

View File

@@ -138,12 +138,12 @@ static UPNP_INLINE void scanner_init(OUT scanner_t *scanner, IN membuffer *bufpt
* Parameters :
* IN char c ; character to be tested against used separator values
*
* Description : Finds the separator character.
* Description : Determines if the passed value is a separator
*
************************************************************************/
static UPNP_INLINE int is_separator_char(IN char c)
static UPNP_INLINE int is_separator_char(IN int c)
{
return strchr(" \t()<>@,;:\\\"/[]?={}", (int)c) != NULL;
return strchr(" \t()<>@,;:\\\"/[]?={}", c) != 0;
}
/************************************************************************
@@ -152,10 +152,10 @@ static UPNP_INLINE int is_separator_char(IN char c)
* Parameters :
* IN char c ; character to be tested for separator values
*
* Description : Calls the function to indentify separator character
* Description : Determines if the passed value is permissible in token
*
************************************************************************/
static UPNP_INLINE int is_identifier_char(IN char c)
static UPNP_INLINE int is_identifier_char(IN int c)
{
return c >= 32 && c <= 126 && !is_separator_char(c);
}
@@ -169,7 +169,7 @@ static UPNP_INLINE int is_identifier_char(IN char c)
* Description : Determines if the passed value is a control character
*
************************************************************************/
static UPNP_INLINE int is_control_char(IN char c)
static UPNP_INLINE int is_control_char(IN int c)
{
return (c >= 0 && c <= 31) || c == 127;
}
@@ -180,23 +180,20 @@ static UPNP_INLINE int is_control_char(IN char c)
* Parameters :
* IN char cc ; character to be tested for CR/LF
*
* Description : Checks to see if the passed in value is CR/LF
* Description : Determines if the passed value is permissible in qdtext
*
************************************************************************/
static UPNP_INLINE int is_qdtext_char(IN char cc)
static UPNP_INLINE int is_qdtext_char(IN int c)
{
unsigned char c = ( unsigned char )cc;
/* we don't check for this; it's checked in get_token() */
assert( c != '"' );
/* we don't check for this; it's checked in get_token() */
assert( c != '"' );
if( ( c >= 32 && c != 127 ) ||
( c == TOKCHAR_CR || c == TOKCHAR_LF || c == '\t' )
) {
return TRUE;
} else {
return FALSE;
}
return
(c >= 32 && c != 127) ||
c < 0 ||
c == TOKCHAR_CR ||
c == TOKCHAR_LF ||
c == '\t';
}
/************************************************************************
@@ -224,7 +221,7 @@ static parse_status_t scanner_get_token(
{
char *cursor;
char *null_terminator; /* point to null-terminator in buffer */
char c;
int c;
token_type_t token_type;
int got_end_quote;
@@ -243,7 +240,7 @@ static parse_status_t scanner_get_token(
/* scan identifier */
token->buf = cursor++;
token_type = TT_IDENTIFIER;
while (is_identifier_char(*cursor))
while (cursor < null_terminator && is_identifier_char(*cursor))
cursor++;
if (!scanner->entire_msg_loaded && cursor == null_terminator)
/* possibly more valid chars */
@@ -253,7 +250,7 @@ static parse_status_t scanner_get_token(
} else if (c == ' ' || c == '\t') {
token->buf = cursor++;
token_type = TT_WHITESPACE;
while (*cursor == ' ' || *cursor == '\t')
while (cursor < null_terminator && (*cursor == ' ' || *cursor == '\t'))
cursor++;
if (!scanner->entire_msg_loaded && cursor == null_terminator)
/* possibly more chars */
@@ -292,9 +289,7 @@ static parse_status_t scanner_get_token(
} else if (c == '\\') {
if (cursor < null_terminator) {
c = *cursor++;
/*if ( !(c > 0 && c <= 127) ) */
if (c == 0)
return PARSE_FAILURE;
/* the char after '\\' could be ANY octet */
}
/* else, while loop handles incomplete buf */
} else if (is_qdtext_char(c)) {
@@ -521,8 +516,9 @@ http_header_t *httpmsg_find_hdr(
* Description : skips blank lines at the start of a msg.
*
* Return : parse_status_t ;
*
* Note :
* PARSE_OK
* PARSE_INCOMPLETE -- not enuf chars to get a token
* PARSE_FAILURE -- bad msg format
************************************************************************/
static UPNP_INLINE parse_status_t skip_blank_lines(INOUT scanner_t *scanner)
{
@@ -1132,6 +1128,7 @@ static parse_status_t vfmatch(
* PARSE_OK
* PARSE_NO_MATCH
* PARSE_INCOMPLETE
* PARSE_FAILURE - bad input
************************************************************************/
static parse_status_t match(
INOUT scanner_t *scanner,
@@ -1164,6 +1161,7 @@ static parse_status_t match(
* PARSE_OK
* PARSE_NO_MATCH -- failure to match pattern 'fmt'
* PARSE_FAILURE -- 'str' is bad input
* PARSE_INCOMPLETE
************************************************************************/
parse_status_t
matchstr( IN char *str,
@@ -1238,6 +1236,8 @@ parser_init( OUT http_parser_t * parser )
* PARSE_OK
* PARSE_SUCCESS
* PARSE_FAILURE
* PARSE_INCOMPLETE
* PARSE_NO_MATCH
************************************************************************/
static parse_status_t
parser_parse_requestline( INOUT http_parser_t * parser )
@@ -1354,12 +1354,13 @@ parser_parse_requestline( INOUT http_parser_t * parser )
* Parameters:
* INOUT http_parser_t* parser ; HTTP Parser object
*
* Description: Get HTTP Method, URL location and version information.
* Description: Get HTTP version information, status code and status msg.
*
* Returns:
* PARSE_OK
* PARSE_SUCCESS
* PARSE_FAILURE
* PARSE_INCOMPLETE
* PARSE_NO_MATCH
************************************************************************/
parse_status_t parser_parse_responseline(INOUT http_parser_t *parser)
{
@@ -1431,12 +1432,14 @@ parse_status_t parser_parse_responseline(INOUT http_parser_t *parser)
* Parameters:
* INOUT http_parser_t* parser ; HTTP Parser object
*
* Description: Get HTTP Method, URL location and version information.
* Description: Read HTTP header fields.
*
* Returns:
* PARSE_OK
* PARSE_SUCCESS
* PARSE_FAILURE
* PARSE_INCOMPLETE
* PARSE_NO_MATCH
************************************************************************/
parse_status_t parser_parse_headers(INOUT http_parser_t *parser)
{
@@ -1462,6 +1465,8 @@ parse_status_t parser_parse_headers(INOUT http_parser_t *parser)
/* check end of headers */
status = scanner_get_token(scanner, &token, &tok_type);
if (status != (parse_status_t)PARSE_OK) {
/* pushback tokens; useful only on INCOMPLETE error */
scanner->cursor = save_pos;
return status;
}
switch (tok_type) {
@@ -1529,6 +1534,8 @@ parse_status_t parser_parse_headers(INOUT http_parser_t *parser)
if (membuffer_assign(&header->name_buf, token.buf, token.length) ||
membuffer_assign(&header->value, hdr_value.buf, hdr_value.length)) {
/* not enough mem */
membuffer_destroy(&header->value);
membuffer_destroy(&header->name_buf);
free(header);
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
return PARSE_FAILURE;
@@ -1536,15 +1543,13 @@ parse_status_t parser_parse_headers(INOUT http_parser_t *parser)
header->name.buf = header->name_buf.buf;
header->name.length = header->name_buf.length;
header->name_id = header_id;
ListAddTail(&parser->msg.headers, header);
/*NNS: ret = dlist_append( &parser->msg.headers, header ); */
/** TODO: remove that? Yes as ret is not set anymore
if (ret == UPNP_E_OUTOF_MEMORY) {
parser->http_error_code =
HTTP_INTERNAL_SERVER_ERROR;
if (!ListAddTail(&parser->msg.headers, header)) {
membuffer_destroy(&header->value);
membuffer_destroy(&header->name_buf);
free(header);
parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
return PARSE_FAILURE;
}
end of remove that? */
} else if (hdr_value.length > (size_t)0) {
/* append value to existing header */
/* append space */
@@ -1575,7 +1580,6 @@ end of remove that? */
*
* Returns:
* PARSE_INCOMPLETE
* PARSE_FAILURE -- entity length > content-length value
* PARSE_SUCCESS
************************************************************************/
static UPNP_INLINE parse_status_t
@@ -1622,9 +1626,10 @@ parser_parse_entity_using_clen( INOUT http_parser_t * parser )
* Description: Read data in the chunks
*
* Returns:
* PARSE_CONTINUE_1
* PARSE_INCOMPLETE
* PARSE_FAILURE -- entity length > content-length value
* PARSE_SUCCESS
* PARSE_FAILURE
* PARSE_NO_MATCH
************************************************************************/
static UPNP_INLINE parse_status_t parser_parse_chunky_body(
INOUT http_parser_t *parser)
@@ -1666,8 +1671,9 @@ static UPNP_INLINE parse_status_t parser_parse_chunky_body(
* Description: Read headers at the end of the chunked entity
*
* Returns:
* PARSE_NO_MATCH
* PARSE_INCOMPLETE
* PARSE_FAILURE -- entity length > content-length value
* PARSE_FAILURE
* PARSE_SUCCESS
************************************************************************/
static UPNP_INLINE parse_status_t
@@ -1702,12 +1708,12 @@ parser_parse_chunky_headers( INOUT http_parser_t * parser )
* Parameters:
* INOUT http_parser_t* parser - HTTP Parser Object
*
* Description: Read headers at the end of the chunked entity
* Description: Read entity using chunked transfer encoding
*
* Returns:
* PARSE_INCOMPLETE
* PARSE_FAILURE -- entity length > content-length value
* PARSE_SUCCESS
* PARSE_FAILURE
* PARSE_NO_MATCH
* PARSE_CONTINUE_1
************************************************************************/
static UPNP_INLINE parse_status_t
@@ -1756,7 +1762,7 @@ parser_parse_chunky_entity( INOUT http_parser_t * parser )
* Parameters:
* INOUT http_parser_t* parser ; HTTP Parser object
*
* Description: Read headers at the end of the chunked entity
* Description: Keep reading entity until the connection is closed.
*
* Returns:
* PARSE_INCOMPLETE_ENTITY
@@ -1793,9 +1799,9 @@ parser_parse_entity_until_close( INOUT http_parser_t * parser )
* Description: Determines method to read entity
*
* Returns:
* PARSE_OK
* PARSE_CONTINUE_1
* PARSE_FAILURE
* PARSE_COMPLETE -- no more reading to do
* PARSE_SUCCESS -- no more reading to do
************************************************************************/
UPNP_INLINE parse_status_t
parser_get_entity_read_method( INOUT http_parser_t * parser )
@@ -1891,17 +1897,19 @@ parser_get_entity_read_method( INOUT http_parser_t * parser )
* Parameters:
* INOUT http_parser_t* parser ; HTTP Parser object
*
* Description: Determines method to read entity
* Description: Read HTTP entity body
*
* Returns:
* PARSE_OK
* PARSE_FAILURE
* PARSE_COMPLETE -- no more reading to do
* PARSE_NO_MATCH
* PARSE_INCOMPLETE
* PARSE_INCOMPLETE_ENTITY
* PARSE_SUCCESS -- no more reading to do
************************************************************************/
UPNP_INLINE parse_status_t
parser_parse_entity( INOUT http_parser_t * parser )
{
parse_status_t status = PARSE_OK;
parse_status_t status;
assert( parser->position == POS_ENTITY );
@@ -1932,6 +1940,7 @@ parser_parse_entity( INOUT http_parser_t * parser )
break;
default:
status = PARSE_FAILURE;
assert( 0 );
}
@@ -1992,7 +2001,11 @@ parser_response_init( OUT http_parser_t * parser,
* parser object the actual parsing function is invoked
*
* Returns:
* void
* PARSE_SUCCESS
* PARSE_FAILURE
* PARSE_INCOMPLETE
* PARSE_INCOMPLETE_ENTITY
* PARSE_NO_MATCH
************************************************************************/
parse_status_t
parser_parse( INOUT http_parser_t * parser )
@@ -2047,11 +2060,14 @@ parser_parse( INOUT http_parser_t * parser )
* buffer
* IN size_t buf_length ; Size of the buffer
*
* Description: The parser function. Depending on the position of the
* parser object the actual parsing function is invoked
* Description: Append date to HTTP parser, and do the parsing.
*
* Returns:
* void
* PARSE_SUCCESS
* PARSE_FAILURE
* PARSE_INCOMPLETE
* PARSE_INCOMPLETE_ENTITY
* PARSE_NO_MATCH
************************************************************************/
parse_status_t
parser_append( INOUT http_parser_t * parser,

View File

@@ -322,6 +322,7 @@ int http_RecvMessage(
ret = 0;
goto ExitFunction;
case PARSE_FAILURE:
case PARSE_NO_MATCH:
*http_error_code = parser->http_error_code;
line = __LINE__;
ret = UPNP_E_BAD_HTTPMSG;
@@ -480,13 +481,12 @@ int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt, ...)
memset(Chunk_Header, 0,
sizeof(Chunk_Header));
rc = snprintf(Chunk_Header,
sizeof(Chunk_Header) - strlen ("\r\n"),
"%" PRIzx, num_read);
if (rc < 0 || (unsigned int) rc >= sizeof(Chunk_Header) - strlen ("\r\n")) {
sizeof(Chunk_Header),
"%" PRIzx "\r\n", num_read);
if (rc < 0 || (unsigned int) rc >= sizeof(Chunk_Header)) {
RetVal = UPNP_E_INTERNAL_ERROR;
goto Cleanup_File;
}
strncat(Chunk_Header, "\r\n", strlen ("\r\n"));
/* Copy the chunk size header */
memcpy(file_buf - strlen(Chunk_Header),
Chunk_Header,

View File

@@ -88,14 +88,19 @@ static const char *Http4xxStr =
"Unsupported Media Type\0"
"Requested Range Not Satisfiable\0" "Expectation Failed\0";
#define NUM_5XX_CODES 6
#define NUM_5XX_CODES 11
static const char *Http5xxCodes[NUM_5XX_CODES];
static const char *Http5xxStr =
"Internal Server Error\0"
"Not Implemented\0"
"Bad Gateway\0"
"Service Unavailable\0"
"Gateway Timeout\0" "HTTP Version Not Supported\0";
"Gateway Timeout\0"
"HTTP Version Not Supported\0"
"Variant Also Negotiates\0"
"Insufficient Storage\0"
"Loop Detected\0"
"\0" "Not Extended\0";
static int gInitialized = FALSE;

View File

@@ -129,7 +129,7 @@ static const char *gMediaTypes[] = {
#define TEXT_INDEX 5
/* general */
#define NUM_MEDIA_TYPES 69
#define NUM_MEDIA_TYPES 70
#define NUM_HTTP_HEADER_NAMES 33
#define ASCTIME_R_BUFFER_SIZE 26
@@ -156,6 +156,7 @@ static const char *gEncodedMediaTypes =
"au\0" AUDIO_STR "basic\0"
"avi\0" VIDEO_STR "msvideo\0"
"bmp\0" IMAGE_STR "bmp\0"
"css\0" TEXT_STR "css\0"
"dcr\0" APPLICATION_STR "x-director\0"
"dib\0" IMAGE_STR "bmp\0"
"dir\0" APPLICATION_STR "x-director\0"
@@ -771,7 +772,7 @@ static int GetNextRange(
*
* \return
* \li \c HTTP_BAD_REQUEST
* \li \c UPNP_E_OUTOF_MEMORY
* \li \c HTTP_INTERNAL_SERVER_ERROR
* \li \c HTTP_REQUEST_RANGE_NOT_SATISFIABLE
* \li \c HTTP_OK
*/
@@ -792,11 +793,9 @@ static int CreateHTTPRangeResponseHeader(
Instr->ReadSendSize = FileLength;
if (!ByteRangeSpecifier)
return HTTP_BAD_REQUEST;
RangeInput = malloc(strlen(ByteRangeSpecifier) + 1);
RangeInput = strdup(ByteRangeSpecifier);
if (!RangeInput)
return UPNP_E_OUTOF_MEMORY;
memset(RangeInput, 0, strlen(ByteRangeSpecifier) + 1);
strncpy(RangeInput, ByteRangeSpecifier, strlen(ByteRangeSpecifier));
return HTTP_INTERNAL_SERVER_ERROR;
/* CONTENT-RANGE: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
if (StrStr(RangeInput, "bytes") == NULL ||
(Ptr = StrStr(RangeInput, "=")) == NULL) {
@@ -830,7 +829,7 @@ static int CreateHTTPRangeResponseHeader(
(int64_t)FileLength);
if (rc < 0 || (unsigned int) rc >= sizeof(Instr->RangeHeader)) {
free(RangeInput);
return UPNP_E_OUTOF_MEMORY;
return HTTP_INTERNAL_SERVER_ERROR;
}
} else if (FirstByte >= 0 && LastByte == -1
&& FirstByte < FileLength) {
@@ -845,7 +844,7 @@ static int CreateHTTPRangeResponseHeader(
(int64_t)FileLength);
if (rc < 0 || (unsigned int) rc >= sizeof(Instr->RangeHeader)) {
free(RangeInput);
return UPNP_E_OUTOF_MEMORY;
return HTTP_INTERNAL_SERVER_ERROR;
}
} else if (FirstByte == -1 && LastByte > 0) {
if (LastByte >= FileLength) {
@@ -864,13 +863,13 @@ static int CreateHTTPRangeResponseHeader(
sizeof(Instr->RangeHeader),
"CONTENT-RANGE: bytes %" PRId64
"-%" PRId64 "/%" PRId64 "\r\n",
(int64_t)(FileLength - LastByte + 1),
(int64_t)FileLength,
(int64_t)(FileLength - LastByte),
(int64_t)FileLength - 1,
(int64_t)FileLength);
}
if (rc < 0 || (unsigned int) rc >= sizeof(Instr->RangeHeader)) {
free(RangeInput);
return UPNP_E_OUTOF_MEMORY;
return HTTP_INTERNAL_SERVER_ERROR;
}
} else {
free(RangeInput);
@@ -891,7 +890,7 @@ static int CreateHTTPRangeResponseHeader(
*
* \return
* \li \c HTTP_BAD_REQUEST
* \li \c UPNP_E_OUTOF_MEMORY
* \li \c HTTP_INTERNAL_SERVER_ERROR
* \li \c HTTP_REQUEST_RANGE_NOT_SATISFIABLE
* \li \c HTTP_OK
*/
@@ -912,7 +911,7 @@ static int CheckOtherHTTPHeaders(
TmpBuf = (char *)malloc(TmpBufSize);
if (!TmpBuf)
return UPNP_E_OUTOF_MEMORY;
return HTTP_INTERNAL_SERVER_ERROR;
node = ListHead(&Req->headers);
while (node != NULL) {
header = (http_header_t *) node->item;
@@ -925,7 +924,7 @@ static int CheckOtherHTTPHeaders(
TmpBufSize = header->value.length + 1;
TmpBuf = (char *)malloc(TmpBufSize);
if (!TmpBuf)
return UPNP_E_OUTOF_MEMORY;
return HTTP_INTERNAL_SERVER_ERROR;
}
memcpy(TmpBuf, header->value.buf, header->value.length);
TmpBuf[header->value.length] = '\0';
@@ -1014,8 +1013,11 @@ static int CheckOtherHTTPHeaders(
*
* \return
* \li \c HTTP_BAD_REQUEST
* \li \c UPNP_E_OUTOF_MEMORY
* \li \c HTTP_INTERNAL_SERVER_ERROR
* \li \c HTTP_REQUEST_RANGE_NOT_SATISFIABLE
* \li \c HTTP_FORBIDDEN
* \li \c HTTP_NOT_FOUND
* \li \c HTTP_NOT_ACCEPTABLE
* \li \c HTTP_OK
*/
static int process_request(
@@ -1202,14 +1204,15 @@ static int process_request(
}
RespInstr->ReadSendSize = finfo.file_length;
/* Check other header field. */
if ((err_code =
if ((code =
CheckOtherHTTPHeaders(req, RespInstr,
finfo.file_length)) != HTTP_OK) {
err_code = code;
goto error_handler;
}
if (req->method == HTTPMETHOD_POST) {
*rtype = RESP_POST;
err_code = UPNP_E_SUCCESS;
err_code = HTTP_OK;
goto error_handler;
}
/*extra_headers = UpnpFileInfo_get_ExtraHeaders(finfo); */
@@ -1247,7 +1250,6 @@ static int process_request(
}
} else if (RespInstr->IsRangeActive && !RespInstr->IsChunkActive) {
/* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
/* Transfer-Encoding: chunked */
if (http_MakeMessage(headers, resp_major, resp_minor,
"R" "N" "T" "GLD" "s" "tcS" "Xc" "sCc",
HTTP_PARTIAL_CONTENT, /* status code */
@@ -1261,7 +1263,6 @@ static int process_request(
goto error_handler;
}
} else if (!RespInstr->IsRangeActive && RespInstr->IsChunkActive) {
/* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
/* Transfer-Encoding: chunked */
if (http_MakeMessage(headers, resp_major, resp_minor,
"RK" "TLD" "s" "tcS" "Xc" "sCc",
@@ -1276,8 +1277,6 @@ static int process_request(
} else {
/* !RespInstr->IsRangeActive && !RespInstr->IsChunkActive */
if (RespInstr->ReadSendSize >= 0) {
/* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
/* Transfer-Encoding: chunked */
if (http_MakeMessage(headers, resp_major, resp_minor,
"R" "N" "TLD" "s" "tcS" "Xc" "sCc",
HTTP_OK, /* status code */
@@ -1291,8 +1290,6 @@ static int process_request(
goto error_handler;
}
} else {
/* Content-Range: bytes 222-3333/4000 HTTP_PARTIAL_CONTENT */
/* Transfer-Encoding: chunked */
if (http_MakeMessage(headers, resp_major, resp_minor,
"R" "TLD" "s" "tcS" "Xc" "sCc",
HTTP_OK, /* status code */
@@ -1322,12 +1319,12 @@ static int process_request(
if (req->method == HTTPMETHOD_SIMPLEGET) {
membuffer_destroy(headers);
}
err_code = UPNP_E_SUCCESS;
err_code = HTTP_OK;
error_handler:
free(request_doc);
ixmlFreeDOMString(finfo.content_type);
if (err_code != UPNP_E_SUCCESS && alias_grabbed) {
if (err_code != HTTP_OK && alias_grabbed) {
alias_release(alias);
}
@@ -1340,7 +1337,8 @@ static int process_request(
* \return
* \li \c HTTP_INTERNAL_SERVER_ERROR
* \li \c HTTP_UNAUTHORIZED
* \li \c HTTP_REQUEST_RANGE_NOT_SATISFIABLE
* \li \c HTTP_BAD_REQUEST
* \li \c HTTP_SERVICE_UNAVAILABLE
* \li \c HTTP_OK
*/
static int http_RecvPostMessage(
@@ -1426,7 +1424,7 @@ static int http_RecvPostMessage(
goto ExitFunction;
}
} else {
ret_code = num_read;
ret_code = HTTP_SERVICE_UNAVAILABLE;
goto ExitFunction;
}
}
@@ -1489,7 +1487,7 @@ void web_server_callback(http_parser_t *parser, INOUT http_message_t *req,
/*the type of request. */
ret = process_request(req, &rtype, &headers, &filename, &xmldoc,
&RespInstr);
if (ret != UPNP_E_SUCCESS) {
if (ret != HTTP_OK) {
/* send error code */
http_SendStatusResponse(info, ret, req->major_version,
req->minor_version);
@@ -1531,7 +1529,7 @@ void web_server_callback(http_parser_t *parser, INOUT http_message_t *req,
/* Send response. */
http_MakeMessage(&headers, 1, 1,
"RTLSXcCc",
ret, "text/html", X_USER_AGENT);
ret, "text/html", &RespInstr, X_USER_AGENT);
http_SendMessage(info, &timeout, "b",
headers.buf, headers.length);
break;

View File

@@ -387,7 +387,7 @@ static int parse_hostport(
ret = getaddrinfo(srvname, NULL, &hints, &res0);
if (ret == 0) {
for (res = res0; res && !ret; res = res->ai_next) {
for (res = res0; res; res = res->ai_next) {
switch (res->ai_family) {
case AF_INET:
case AF_INET6:
@@ -395,12 +395,10 @@ static int parse_hostport(
memcpy(&out->IPaddress,
res->ai_addr,
res->ai_addrlen);
ret=1;
break;
default:
break;
goto found;
}
}
found:
freeaddrinfo(res0);
if (res == NULL)
/* Didn't find an AF_INET or AF_INET6 address. */
@@ -512,68 +510,81 @@ int remove_escaped_chars(INOUT char *in, INOUT size_t *size)
}
int remove_dots(char *in, size_t size)
static UPNP_INLINE int is_end_path(char c) {
switch (c) {
case '?':
case '#':
case '\0':
return 1;
}
return 0;
}
/* This function directly implements the "Remove Dot Segments"
* algorithm described in RFC 3986 section 5.2.4. */
int remove_dots(char *buf, size_t size)
{
char *copyTo = in;
char *copyFrom = in;
char *max = in + size;
char **Segments = NULL;
int lastSegment = -1;
char *in = buf;
char *out = buf;
char *max = buf + size;
Segments = malloc( sizeof( char * ) * size );
while (!is_end_path(in[0])) {
assert (buf <= out);
assert (out <= in);
assert (in < max);
if( Segments == NULL )
return UPNP_E_OUTOF_MEMORY;
Segments[0] = NULL;
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
"REMOVE_DOTS: before: %s\n", in );
while( ( copyFrom < max ) && ( *copyFrom != '?' )
&& ( *copyFrom != '#' ) ) {
if( ( ( *copyFrom ) == '.' )
&& ( ( copyFrom == in ) || ( *( copyFrom - 1 ) == '/' ) ) ) {
if( ( copyFrom + 1 == max )
|| ( *( copyFrom + 1 ) == '/' ) ) {
copyFrom += 2;
continue;
} else if( ( *( copyFrom + 1 ) == '.' )
&& ( ( copyFrom + 2 == max )
|| ( *( copyFrom + 2 ) == '/' ) ) ) {
copyFrom += 3;
if( lastSegment > 0 ) {
copyTo = Segments[--lastSegment];
} else {
free( Segments );
/*TRACE("ERROR RESOLVING URL, ../ at ROOT"); */
return UPNP_E_INVALID_URL;
}
continue;
/* case 2.A: */
if (strncmp(in, "./", 2) == 0) {
in += 2;
} else if (strncmp(in, "../", 3) == 0) {
in += 3;
/* case 2.B: */
} else if (strncmp(in, "/./", 3) == 0) {
in += 2;
} else if (strncmp(in, "/.", 2) == 0 && is_end_path(in[2])) {
in += 1;
in[0] = '/';
/* case 2.C: */
} else if (strncmp(in, "/../", 4) == 0 || (strncmp(in, "/..", 3) == 0 && is_end_path(in[3]))) {
/* Make the next character in the input buffer a '/': */
if (is_end_path(in[3])) { /* terminating "/.." case */
in += 2;
in[0] = '/';
} else { /* "/../" prefix case */
in += 3;
}
}
if( ( *copyFrom ) == '/' ) {
lastSegment++;
Segments[lastSegment] = copyTo + 1;
}
( *copyTo ) = ( *copyFrom );
copyTo++;
copyFrom++;
}
if( copyFrom < max ) {
while( copyFrom < max ) {
( *copyTo ) = ( *copyFrom );
copyTo++;
copyFrom++;
/* Trim the last component from the output buffer, or empty it. */
while (buf < out)
if (*--out == '/')
break;
#ifdef DEBUG
if (out < in)
out[0] = '\0';
#endif
/* case 2.D: */
} else if (strncmp(in, ".", 1) == 0 && is_end_path(in[1])) {
in += 1;
} else if (strncmp(in, "..", 2) == 0 && is_end_path(in[2])) {
in += 2;
/* case 2.E */
} else {
/* move initial '/' character (if any) */
if (in[0] == '/')
*out++ = *in++;
/* move first segment up to, but not including, the next '/' character */
while (in < max && in[0] != '/' && !is_end_path(in[0]))
*out++ = *in++;
#ifdef DEBUG
if (out < in)
out[0] = '\0';
#endif
}
}
( *copyTo ) = 0;
free( Segments );
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
"REMOVE_DOTS: after: %s\n", in );
while (in < max)
*out++ = *in++;
if (out < max)
out[0] = '\0';
return UPNP_E_SUCCESS;
}
@@ -582,115 +593,113 @@ char *resolve_rel_url(char *base_url, char *rel_url)
{
uri_type base;
uri_type rel;
int rv;
size_t i = (size_t)0;
char *finger = NULL;
char *last_slash = NULL;
char *out = NULL;
if( base_url && rel_url ) {
out =
( char * )malloc( strlen( base_url ) + strlen( rel_url ) + (size_t)2 );
} else {
if( rel_url )
return strdup( rel_url );
else
if (!base_url) {
if (!rel_url)
return NULL;
return strdup(rel_url);
}
if( out == NULL ) {
size_t len_rel = strlen(rel_url);
if (parse_uri(rel_url, len_rel, &rel) != HTTP_SUCCESS)
return NULL;
if (rel.type == (enum uriType)ABSOLUTE)
return strdup(rel_url);
size_t len_base = strlen(base_url);
if ((parse_uri(base_url, len_base, &base) != HTTP_SUCCESS)
|| (base.type != (enum uriType)ABSOLUTE))
return NULL;
if (len_rel == (size_t)0)
return strdup(base_url);
size_t len = len_base + len_rel + (size_t)2;
char *out = (char *)malloc(len);
if (out == NULL)
return NULL;
memset(out, 0, len);
char *out_finger = out;
/* scheme */
rv = snprintf(out_finger, len, "%.*s:", (int)base.scheme.size, base.scheme.buff);
if (rv < 0 || rv >= len)
goto error;
out_finger += rv;
len -= rv;
/* authority */
if (rel.hostport.text.size > (size_t)0) {
rv = snprintf(out_finger, len, "%s", rel_url);
if (rv < 0 || rv >= len)
goto error;
return out;
}
if (base.hostport.text.size > (size_t)0) {
rv = snprintf(out_finger, len, "//%.*s", (int)base.hostport.text.size, base.hostport.text.buff);
if (rv < 0 || rv >= len)
goto error;
out_finger += rv;
len -= rv;
}
memset( out, 0, strlen( base_url ) + strlen( rel_url ) + (size_t)2 );
if( ( parse_uri( rel_url, strlen( rel_url ), &rel ) ) == HTTP_SUCCESS ) {
if( rel.type == ( enum uriType) ABSOLUTE ) {
strncpy( out, rel_url, strlen ( rel_url ) );
} else {
if( ( parse_uri( base_url, strlen( base_url ), &base ) ==
HTTP_SUCCESS )
&& ( base.type == ( enum uriType ) ABSOLUTE ) ) {
if( strlen( rel_url ) == (size_t)0 ) {
strncpy( out, base_url, strlen ( base_url ) );
} else {
char *out_finger = out;
assert( base.scheme.size + (size_t)1 /* ':' */ <= strlen ( base_url ) );
memcpy( out, base.scheme.buff, base.scheme.size );
out_finger += base.scheme.size;
( *out_finger ) = ':';
out_finger++;
if( rel.hostport.text.size > (size_t)0 ) {
snprintf( out_finger, strlen( rel_url ) + (size_t)1,
"%s", rel_url );
} else {
if( base.hostport.text.size > (size_t)0 ) {
assert( base.scheme.size + (size_t)1
+ base.hostport.text.size + (size_t)2 /* "//" */ <= strlen ( base_url ) );
memcpy( out_finger, "//", (size_t)2 );
out_finger += 2;
memcpy( out_finger, base.hostport.text.buff,
base.hostport.text.size );
out_finger += base.hostport.text.size;
}
if( rel.path_type == ( enum pathType ) ABS_PATH ) {
strncpy( out_finger, rel_url, strlen ( rel_url ) );
} else {
char temp_path = '/';
if( base.pathquery.size == (size_t)0 ) {
base.pathquery.size = (size_t)1;
base.pathquery.buff = &temp_path;
}
assert( base.scheme.size + (size_t)1 + base.hostport.text.size + (size_t)2
+ base.pathquery.size <= strlen ( base_url ) + (size_t)1 /* temp_path */);
finger = out_finger;
last_slash = finger;
i = (size_t)0;
while( ( i < base.pathquery.size ) &&
( base.pathquery.buff[i] != '?' ) ) {
( *finger ) = base.pathquery.buff[i];
if( base.pathquery.buff[i] == '/' )
last_slash = finger + 1;
i++;
finger++;
}
strncpy( last_slash, rel_url, strlen ( rel_url ) );
if( remove_dots( out_finger,
strlen( out_finger ) ) !=
UPNP_E_SUCCESS ) {
free(out);
/* free(rel_url); */
return NULL;
}
}
}
}
} else {
free(out);
/* free(rel_url); */
return NULL;
}
}
/* path */
char *path = out_finger;
if (rel.path_type == (enum pathType)ABS_PATH) {
rv = snprintf(out_finger, len, "%s", rel_url);
} else if (base.pathquery.size == (size_t)0) {
rv = snprintf(out_finger, len, "/%s", rel_url);
} else {
free(out);
/* free(rel_url); */
return NULL;
}
if (rel.pathquery.size == (size_t)0) {
rv = snprintf(out_finger, len, "%.*s", (int)base.pathquery.size, base.pathquery.buff);
} else {
if (len < base.pathquery.size)
goto error;
size_t i = (size_t)0, prefix = (size_t)1;
while (i < base.pathquery.size) {
out_finger[i] = base.pathquery.buff[i];
switch (base.pathquery.buff[i++]) {
case '/':
prefix = i;
/* fall-through */
default:
continue;
case '?': /* query */
if (rel.pathquery.buff[0] == '?')
prefix = --i;
}
break;
}
out_finger += prefix;
len -= prefix;
rv = snprintf(out_finger, len, "%.*s", (int)rel.pathquery.size, rel.pathquery.buff);
}
if (rv < 0 || rv >= len)
goto error;
out_finger += rv;
len -= rv;
/* fragment */
if (rel.fragment.size > (size_t)0)
rv = snprintf(out_finger, len, "#%.*s", (int)rel.fragment.size, rel.fragment.buff);
else if (base.fragment.size > (size_t)0)
rv = snprintf(out_finger, len, "#%.*s", (int)base.fragment.size, base.fragment.buff);
else
rv = 0;
}
if (rv < 0 || rv >= len)
goto error;
out_finger += rv;
len -= rv;
if (remove_dots(path, out_finger - path) != UPNP_E_SUCCESS)
goto error;
/* free(rel_url); */
return out;
error:
free(out);
return NULL;
}

View File

@@ -301,15 +301,16 @@ FindServiceEventURLPath( service_table * table,
if( ( table )
&&
( parse_uri
( eventURLPath, strlen( eventURLPath ), &parsed_url_in ) ) ) {
( parse_uri( eventURLPath,
strlen( eventURLPath ),
&parsed_url_in ) == HTTP_SUCCESS ) ) {
finger = table->serviceList;
while( finger ) {
if( finger->eventURL )
if( ( parse_uri
( finger->eventURL, strlen( finger->eventURL ),
&parsed_url ) ) ) {
&parsed_url ) == HTTP_SUCCESS ) ) {
if( !token_cmp
( &parsed_url.pathquery,
@@ -354,13 +355,13 @@ FindServiceControlURLPath( service_table * table,
&&
( parse_uri
( controlURLPath, strlen( controlURLPath ),
&parsed_url_in ) ) ) {
&parsed_url_in ) == HTTP_SUCCESS ) ) {
finger = table->serviceList;
while( finger ) {
if( finger->controlURL )
if( ( parse_uri
( finger->controlURL, strlen( finger->controlURL ),
&parsed_url ) ) ) {
&parsed_url ) == HTTP_SUCCESS) ) {
if( !token_cmp
( &parsed_url.pathquery,
&parsed_url_in.pathquery ) )

View File

@@ -79,6 +79,10 @@
#define HTTP_SERVICE_UNAVAILABLE 503
#define HTTP_GATEWAY_TIMEOUT 504
#define HTTP_HTTP_VERSION_NOT_SUPPORTED 505
#define HTTP_VARIANT_ALSO_NEGOTIATES 506
#define HTTP_INSUFFICIENT_STORAGE 507
#define HTTP_LOOP_DETECTED 508
#define HTTP_NOT_EXTENDED 510
/* HTTP lib error codes */

View File

@@ -455,7 +455,7 @@ get_response_value( IN http_message_t * hmsg,
if (!temp_str)
goto error_handler;
*upnp_error_code = atoi(temp_str);
if (*upnp_error_code < 400) {
if (*upnp_error_code > 400) {
err_code = *upnp_error_code;
goto error_handler; /* bad SOAP error code */
}

File diff suppressed because it is too large Load Diff

View File

@@ -291,8 +291,9 @@ void ssdp_handle_ctrlpt_msg(http_message_t *hmsg, struct sockaddr_storage *dest_
TPJobSetFreeFunction(&job,
(free_routine)
free);
ThreadPoolAdd(&gRecvThreadPool, &job,
NULL);
if (ThreadPoolAdd(&gRecvThreadPool, &job, NULL) != 0) {
free(threadData);
}
}
}
node = ListNext(&ctrlpt_info->SsdpSearchList, node);

View File

@@ -814,7 +814,7 @@ static int create_ssdp_sock_v4(
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
#if defined(BSD) || defined(__OSX__) || defined(__APPLE__)
#if (defined(BSD) && !defined(__GNU__)) || defined(__OSX__) || defined(__APPLE__)
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
(char *)&onOff, sizeof(onOff));
@@ -956,7 +956,7 @@ static int create_ssdp_sock_v6(
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
#if defined(BSD) || defined(__OSX__) || defined(__APPLE__)
#if (defined(BSD) && !defined(__GNU__)) || defined(__OSX__) || defined(__APPLE__)
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
(char *)&onOff, sizeof(onOff));
@@ -1069,7 +1069,7 @@ static int create_ssdp_sock_v6_ula_gua(
ret = UPNP_E_SOCKET_ERROR;
goto error_handler;
}
#if defined(BSD) || defined(__OSX__) || defined(__APPLE__)
#if (defined(BSD) && !defined(__GNU__)) || defined(__OSX__) || defined(__APPLE__)
onOff = 1;
ret = setsockopt(*ssdpSock, SOL_SOCKET, SO_REUSEPORT,
(char *)&onOff, sizeof(onOff));

View File

@@ -143,15 +143,12 @@ static UPNP_INLINE int calc_alias(
aliasPtr = alias + 1;
else
aliasPtr = alias;
new_alias_len = root_len + strlen(temp_str) + strlen(aliasPtr);
alias_temp = malloc(new_alias_len + (size_t)1);
new_alias_len = root_len + strlen(temp_str) + strlen(aliasPtr) + (size_t)1;
alias_temp = malloc(new_alias_len);
if (alias_temp == NULL)
return UPNP_E_OUTOF_MEMORY;
memset(alias_temp, 0, new_alias_len + (size_t)1);
strncpy(alias_temp, rootPath, root_len);
alias_temp[root_len] = '\0';
strncat(alias_temp, temp_str, strlen(temp_str));
strncat(alias_temp, aliasPtr, strlen(aliasPtr));
memset(alias_temp, 0, new_alias_len);
snprintf(alias_temp, new_alias_len, "%s%s%s", rootPath, temp_str, aliasPtr);
*newAlias = alias_temp;
return UPNP_E_SUCCESS;
@@ -186,14 +183,10 @@ static UPNP_INLINE int calc_descURL(
assert(ipPortStr != NULL && strlen(ipPortStr) > 0);
assert(alias != NULL && strlen(alias) > 0);
len = strlen(http_scheme) + strlen(ipPortStr) + strlen(alias);
if (len > ((size_t)LINE_SIZE - (size_t)1))
len = strlen(http_scheme) + strlen(ipPortStr) + strlen(alias) + (size_t)1;
if (len > (size_t)LINE_SIZE)
return UPNP_E_URL_TOO_BIG;
strncpy(descURL, http_scheme, strlen(http_scheme));
descURL[strlen(http_scheme)] = '\0';
strncat(descURL, ipPortStr, strlen(ipPortStr));
strncat(descURL, alias, strlen(alias));
descURL[len] = '\0';
snprintf(descURL, len, "%s%s%s", http_scheme, ipPortStr, alias);
UpnpPrintf(UPNP_INFO, API, __FILE__, __LINE__,
"desc url: %s\n", descURL);
@@ -267,6 +260,7 @@ static int config_description_doc(
err_code =
ixmlNode_appendChild(rootNode, (IXML_Node *) element);
if (err_code != IXML_SUCCESS) {
err_code = UPNP_E_INVALID_DESC;
goto error_handler;
}
textNode =
@@ -277,6 +271,7 @@ static int config_description_doc(
err_code =
ixmlNode_appendChild((IXML_Node *) element, textNode);
if (err_code != IXML_SUCCESS) {
err_code = UPNP_E_INTERNAL_ERROR;
goto error_handler;
}
} else {
@@ -327,6 +322,7 @@ static int config_description_doc(
}
err_code = ixmlNode_setNodeValue(textNode, url_str.buf);
if (err_code != IXML_SUCCESS) {
err_code = UPNP_E_OUTOF_MEMORY;
goto error_handler;
}
}

131
upnp/test/test_url.c Normal file
View File

@@ -0,0 +1,131 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "upnp.h"
#include "upnptools.h"
struct test {
const char *base;
const char *rel;
const char *expect;
int line;
int error;
};
#define TEST(BaseURL, RelURL, expect, ...) {BaseURL, RelURL, expect, __LINE__, ##__VA_ARGS__}
static int
result(const struct test *test)
{
char *absurl = NULL;
int ret = 0;
ret = UpnpResolveURL2(test->base, test->rel, &absurl);
if (ret == test->error && (test->expect == NULL || strcmp(test->expect, absurl) == 0)) {
ret = 0;
} else {
printf("%s:%d: '%s' | '%s' -> '%s' != '%s' (%d)\n", __FILE__, test->line, test->base, test->rel, absurl, test->expect, ret);
ret = 1;
}
free(absurl);
return ret;
}
/* The URLs must be resolvale! */
static const char ABS_URL1[] = "http://www.libupnp.org/path1/";
static const char ABS_URL2[] = "http://www.libupnp.org/path1/path1";
static const char ABS_URL3[] = "http://localhost/path1/";
static const char ABS_URL4[] = "http://127.0.0.1/path1/";
static const char ABS_URL5[] = "http://127.0.0.1:6544/path1/";
static const char ABS_URL6[] = "http://[::1]:6544/path1/";
static const char REL_URL1[] = "//localhost/path2";
static const char REL_URL2[] = "/path3";
static const char REL_URL3[] = "path4";
static const char REL_URL4[] = "../path5";
static const char REL_URL5[] = "?query1";
static const char REL_URL6[] = "#frag1";
static const char ABS_RFC[] = "http://localhost/b/c/d;p?q";
// s,\<a\>,localhost,
// s,//g\>,//127.0.0.1,
static const struct test RFC3986[] = {
// Errors
TEST(NULL, NULL, NULL, UPNP_E_INVALID_PARAM),
TEST(ABS_URL1, NULL, NULL, UPNP_E_INVALID_PARAM),
TEST("foo", "bar", NULL, UPNP_E_INVALID_URL),
// Custom
TEST(NULL, ABS_URL1, ABS_URL1),
TEST(ABS_URL1, ABS_URL2, ABS_URL2),
TEST(ABS_URL1, "", ABS_URL1),
TEST(ABS_URL1, REL_URL1, "http://localhost/path2"),
TEST(ABS_URL2, REL_URL1, "http://localhost/path2"),
TEST(ABS_URL1, REL_URL2, "http://www.libupnp.org/path3"),
TEST(ABS_URL2, REL_URL2, "http://www.libupnp.org/path3"),
TEST(ABS_URL1, REL_URL3, "http://www.libupnp.org/path1/path4"),
TEST(ABS_URL2, REL_URL3, "http://www.libupnp.org/path1/path4"),
TEST(ABS_URL1, REL_URL4, "http://www.libupnp.org/path5"),
TEST(ABS_URL2, REL_URL4, "http://www.libupnp.org/path5"),
TEST(ABS_URL1, REL_URL6, "http://www.libupnp.org/path1/#frag1"),
TEST(ABS_URL2, REL_URL6, "http://www.libupnp.org/path1/path1#frag1"),
TEST("http://127.0.0.1:6544/getDeviceDesc", "CDS_Event", "http://127.0.0.1:6544/CDS_Event"),
// <http://tools.ietf.org/html/rfc3986#section-5.4.1> Normal Examples
TEST(ABS_RFC, "g:h", "g:h"),
TEST(ABS_RFC, "g", "http://localhost/b/c/g"),
TEST(ABS_RFC, "./g", "http://localhost/b/c/g"),
TEST(ABS_RFC, "g/", "http://localhost/b/c/g/"),
TEST(ABS_RFC, "/g", "http://localhost/g"),
TEST(ABS_RFC, "//127.0.0.1", "http://127.0.0.1"),
TEST(ABS_RFC, "?y", "http://localhost/b/c/d;p?y"),
TEST(ABS_RFC, "g?y", "http://localhost/b/c/g?y"),
TEST(ABS_RFC, "#s", "http://localhost/b/c/d;p?q#s"),
TEST(ABS_RFC, "g#s", "http://localhost/b/c/g#s"),
TEST(ABS_RFC, "g?y#s", "http://localhost/b/c/g?y#s"),
TEST(ABS_RFC, ";x", "http://localhost/b/c/;x"),
TEST(ABS_RFC, "g;x", "http://localhost/b/c/g;x"),
TEST(ABS_RFC, "g;x?y#s", "http://localhost/b/c/g;x?y#s"),
TEST(ABS_RFC, "", "http://localhost/b/c/d;p?q"),
TEST(ABS_RFC, ".", "http://localhost/b/c/"),
TEST(ABS_RFC, "./", "http://localhost/b/c/"),
TEST(ABS_RFC, "..", "http://localhost/b/"),
TEST(ABS_RFC, "../", "http://localhost/b/"),
TEST(ABS_RFC, "../g", "http://localhost/b/g"),
TEST(ABS_RFC, "../..", "http://localhost/"),
TEST(ABS_RFC, "../../", "http://localhost/"),
TEST(ABS_RFC, "../../g", "http://localhost/g"),
// <http://tools.ietf.org/html/rfc3986#section-5.4.2> Abnormal Examples
TEST(ABS_RFC, "../../../g", "http://localhost/g"),
TEST(ABS_RFC, "../../../../g", "http://localhost/g"),
TEST(ABS_RFC, "/./g", "http://localhost/g"),
TEST(ABS_RFC, "/../g", "http://localhost/g"),
TEST(ABS_RFC, "g.", "http://localhost/b/c/g."),
TEST(ABS_RFC, ".g", "http://localhost/b/c/.g"),
TEST(ABS_RFC, "g..", "http://localhost/b/c/g.."),
TEST(ABS_RFC, "..g", "http://localhost/b/c/..g"),
TEST(ABS_RFC, "./../g", "http://localhost/b/g"),
TEST(ABS_RFC, "./g/.", "http://localhost/b/c/g/"),
TEST(ABS_RFC, "g/./h", "http://localhost/b/c/g/h"),
TEST(ABS_RFC, "g/../h", "http://localhost/b/c/h"),
TEST(ABS_RFC, "g;x=1/./y", "http://localhost/b/c/g;x=1/y"),
TEST(ABS_RFC, "g;x=1/../y", "http://localhost/b/c/y"),
TEST(ABS_RFC, "g?y/./x", "http://localhost/b/c/g?y/./x"),
TEST(ABS_RFC, "g?y/../x", "http://localhost/b/c/g?y/../x"),
TEST(ABS_RFC, "g#s/./x", "http://localhost/b/c/g#s/./x"),
TEST(ABS_RFC, "g#s/../x", "http://localhost/b/c/g#s/../x"),
TEST(ABS_RFC, "http:g", "http:g"),
};
#define ARRAY_SIZE(a) (sizeof (a) / sizeof *(a))
int
main (int argc, char* argv[])
{
int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(RFC3986); i++)
ret += result(&RFC3986[i]);
exit (ret ? EXIT_FAILURE : EXIT_SUCCESS);
}
// gcc -o url-test -g url-test.c -I ixml/inc -I threadutil/inc -I upnp/inc upnp/.libs/libupnp.a -L ixml/.libs -lixml -L threadutil/.libs -lthreadutil -lpthread