Browse Source

[tftp] Mangle initial slash on TFTP URIs

TFTP URIs are intrinsically problematic, since:

- TFTP servers may use either normal slashes or backslashes as a
  directory separator,

- TFTP servers allow filenames to be specified using relative paths
  (with no initial directory separator),

- TFTP filenames present in a DHCP filename field may use special
  characters such as "?" or "#" that prevent parsing as a generic URI.

As of commit 7667536 ("[uri] Refactor URI parsing and formatting"), we
have directly constructed TFTP URIs from DHCP next-server and filename
pairs, avoiding the generic URI parser.  This eliminated the problems
related to special characters, but indirectly made it impossible to
parse a "tftp://..." URI string into a TFTP URI with a non-absolute
path.

Re-introduce the convention of requiring an extra slash in a
"tftp://..." URI string in order to specify a TFTP URI with an initial
slash in the filename.  For example:

  tftp://192.168.0.1/boot/pxelinux.0  => RRQ "boot/pxelinux.0"
  tftp://192.168.0.1//boot/pxelinux.0 => RRQ "/boot/pxelinux.0"

This is ugly, but there seems to be no other sensible way to provide
the ability to specify all possible TFTP filenames.

A side-effect of this change is that format_uri() will no longer add a
spurious initial "/" when formatting a relative URI string.  This
improves the console output when fetching an image specified via a
relative URI.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
f0e9e55442
3 changed files with 60 additions and 28 deletions
  1. 51
    21
      src/core/uri.c
  2. 3
    1
      src/net/udp/tftp.c
  3. 6
    6
      src/tests/uri_test.c

+ 51
- 21
src/core/uri.c View File

368
 		goto done;
368
 		goto done;
369
 
369
 
370
 	/* Identify net/absolute/relative path */
370
 	/* Identify net/absolute/relative path */
371
-	if ( strncmp ( path, "//", 2 ) == 0 ) {
371
+	if ( uri->scheme && ( strncmp ( path, "//", 2 ) == 0 ) ) {
372
 		/* Net path.  If this is terminated by the first '/'
372
 		/* Net path.  If this is terminated by the first '/'
373
 		 * of an absolute path, then we have no space for a
373
 		 * of an absolute path, then we have no space for a
374
 		 * terminator after the authority field, so shuffle
374
 		 * terminator after the authority field, so shuffle
459
 		[URI_OPAQUE] = ':',
459
 		[URI_OPAQUE] = ':',
460
 		[URI_PASSWORD] = ':',
460
 		[URI_PASSWORD] = ':',
461
 		[URI_PORT] = ':',
461
 		[URI_PORT] = ':',
462
-		[URI_PATH] = '/',
463
 		[URI_QUERY] = '?',
462
 		[URI_QUERY] = '?',
464
 		[URI_FRAGMENT] = '#',
463
 		[URI_FRAGMENT] = '#',
465
 	};
464
 	};
486
 		prefix = prefixes[field];
485
 		prefix = prefixes[field];
487
 		if ( ( field == URI_HOST ) && ( uri->user != NULL ) )
486
 		if ( ( field == URI_HOST ) && ( uri->user != NULL ) )
488
 			prefix = '@';
487
 			prefix = '@';
489
-		if ( ( field == URI_PATH ) && ( uri->path[0] == '/' ) )
490
-			prefix = '\0';
491
 		if ( prefix ) {
488
 		if ( prefix ) {
492
 			used += ssnprintf ( ( buf + used ), ( len - used ),
489
 			used += ssnprintf ( ( buf + used ), ( len - used ),
493
 					    "%c", prefix );
490
 					    "%c", prefix );
714
 	return new_uri;
711
 	return new_uri;
715
 }
712
 }
716
 
713
 
714
+/**
715
+ * Construct TFTP URI from server address and filename
716
+ *
717
+ * @v sa_server		Server address
718
+ * @v filename		Filename
719
+ * @ret uri		URI, or NULL on failure
720
+ */
721
+static struct uri * tftp_uri ( struct sockaddr *sa_server,
722
+			       const char *filename ) {
723
+	struct sockaddr_tcpip *st_server =
724
+		( ( struct sockaddr_tcpip * ) sa_server );
725
+	char buf[ 6 /* "65535" + NUL */ ];
726
+	char *path;
727
+	struct uri tmp;
728
+	struct uri *uri = NULL;
729
+
730
+	/* Initialise TFTP URI */
731
+	memset ( &tmp, 0, sizeof ( tmp ) );
732
+	tmp.scheme = "tftp";
733
+
734
+	/* Construct TFTP server address */
735
+	tmp.host = sock_ntoa ( sa_server );
736
+	if ( ! tmp.host )
737
+		goto err_host;
738
+
739
+	/* Construct TFTP server port, if applicable */
740
+	if ( st_server->st_port ) {
741
+		snprintf ( buf, sizeof ( buf ), "%d",
742
+			   ntohs ( st_server->st_port ) );
743
+		tmp.port = buf;
744
+	}
745
+
746
+	/* Construct TFTP path */
747
+	if ( asprintf ( &path, "/%s", filename ) < 0 )
748
+		goto err_path;
749
+	tmp.path = path;
750
+
751
+	/* Demangle URI */
752
+	uri = uri_dup ( &tmp );
753
+	if ( ! uri )
754
+		goto err_uri;
755
+
756
+ err_uri:
757
+	free ( path );
758
+ err_path:
759
+ err_host:
760
+	return uri;
761
+}
762
+
717
 /**
763
 /**
718
  * Construct URI from server address and filename
764
  * Construct URI from server address and filename
719
  *
765
  *
727
  * constructing a TFTP URI from the next-server and filename.
773
  * constructing a TFTP URI from the next-server and filename.
728
  */
774
  */
729
 struct uri * pxe_uri ( struct sockaddr *sa_server, const char *filename ) {
775
 struct uri * pxe_uri ( struct sockaddr *sa_server, const char *filename ) {
730
-	char buf[ 6 /* "65535" + NUL */ ];
731
-	struct sockaddr_tcpip *st_server =
732
-		( ( struct sockaddr_tcpip * ) sa_server );
733
-	struct uri tmp;
734
 	struct uri *uri;
776
 	struct uri *uri;
735
 
777
 
736
 	/* Fail if filename is empty */
778
 	/* Fail if filename is empty */
748
 	uri_put ( uri );
790
 	uri_put ( uri );
749
 
791
 
750
 	/* Otherwise, construct a TFTP URI directly */
792
 	/* Otherwise, construct a TFTP URI directly */
751
-	memset ( &tmp, 0, sizeof ( tmp ) );
752
-	tmp.scheme = "tftp";
753
-	tmp.host = sock_ntoa ( sa_server );
754
-	if ( ! tmp.host )
755
-		return NULL;
756
-	if ( st_server->st_port ) {
757
-		snprintf ( buf, sizeof ( buf ), "%d",
758
-			   ntohs ( st_server->st_port ) );
759
-		tmp.port = buf;
760
-	}
761
-	tmp.path = filename;
762
-	uri = uri_dup ( &tmp );
763
-	return uri;
793
+	return tftp_uri ( sa_server, filename );
764
 }
794
 }

+ 3
- 1
src/net/udp/tftp.c View File

325
  * @ret rc		Return status code
325
  * @ret rc		Return status code
326
  */
326
  */
327
 static int tftp_send_rrq ( struct tftp_request *tftp ) {
327
 static int tftp_send_rrq ( struct tftp_request *tftp ) {
328
-	const char *path = tftp->uri->path;
328
+	const char *path = ( tftp->uri->path + 1 /* skip '/' */ );
329
 	struct tftp_rrq *rrq;
329
 	struct tftp_rrq *rrq;
330
 	size_t len;
330
 	size_t len;
331
 	struct io_buffer *iobuf;
331
 	struct io_buffer *iobuf;
1067
 		return -EINVAL;
1067
 		return -EINVAL;
1068
 	if ( ! uri->path )
1068
 	if ( ! uri->path )
1069
 		return -EINVAL;
1069
 		return -EINVAL;
1070
+	if ( uri->path[0] != '/' )
1071
+		return -EINVAL;
1070
 
1072
 
1071
 	/* Allocate and populate TFTP structure */
1073
 	/* Allocate and populate TFTP structure */
1072
 	tftp = zalloc ( sizeof ( *tftp ) );
1074
 	tftp = zalloc ( sizeof ( *tftp ) );

+ 6
- 6
src/tests/uri_test.c View File

713
 	{
713
 	{
714
 		.scheme = "tftp",
714
 		.scheme = "tftp",
715
 		.host = "192.168.0.2",
715
 		.host = "192.168.0.2",
716
-		.path = "/absolute/path",
716
+		.path = "//absolute/path",
717
 	},
717
 	},
718
-	"tftp://192.168.0.2/absolute/path",
718
+	"tftp://192.168.0.2//absolute/path",
719
 };
719
 };
720
 
720
 
721
 /** PXE URI with relative path */
721
 /** PXE URI with relative path */
731
 	{
731
 	{
732
 		.scheme = "tftp",
732
 		.scheme = "tftp",
733
 		.host = "192.168.0.3",
733
 		.host = "192.168.0.3",
734
-		.path = "relative/path",
734
+		.path = "/relative/path",
735
 	},
735
 	},
736
 	"tftp://192.168.0.3/relative/path",
736
 	"tftp://192.168.0.3/relative/path",
737
 };
737
 };
749
 	{
749
 	{
750
 		.scheme = "tftp",
750
 		.scheme = "tftp",
751
 		.host = "10.0.0.6",
751
 		.host = "10.0.0.6",
752
-		.path = "C:\\tftpboot\\icky#path",
752
+		.path = "/C:\\tftpboot\\icky#path",
753
 	},
753
 	},
754
 	"tftp://10.0.0.6/C%3A\\tftpboot\\icky%23path",
754
 	"tftp://10.0.0.6/C%3A\\tftpboot\\icky%23path",
755
 };
755
 };
769
 		.scheme = "tftp",
769
 		.scheme = "tftp",
770
 		.host = "192.168.0.1",
770
 		.host = "192.168.0.1",
771
 		.port = "4069",
771
 		.port = "4069",
772
-		.path = "/another/path",
772
+		.path = "//another/path",
773
 	},
773
 	},
774
-	"tftp://192.168.0.1:4069/another/path",
774
+	"tftp://192.168.0.1:4069//another/path",
775
 };
775
 };
776
 
776
 
777
 /** Current working URI test */
777
 /** Current working URI test */

Loading…
Cancel
Save