tftpd: allow IPv4/6-specific remapping rules

Allow remapping rules to be conditional on IPv4 vs IPv6.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
diff --git a/tftpd/remap.c b/tftpd/remap.c
index 1e7abe7..6f5b409 100644
--- a/tftpd/remap.c
+++ b/tftpd/remap.c
@@ -1,6 +1,6 @@
 /* ----------------------------------------------------------------------- *
  *
- *   Copyright 2001-2007 H. Peter Anvin - All Rights Reserved
+ *   Copyright 2001-2014 H. Peter Anvin - All Rights Reserved
  *
  *   This program is free software available under the same license
  *   as the "OpenBSD" operating system, distributed at
@@ -31,6 +31,8 @@
 #define RULE_RESTART	0x08    /* Restart at the top after matching this rule */
 #define RULE_ABORT	0x10    /* Terminate processing with an error */
 #define RULE_INVERSE	0x20    /* Execute if regex *doesn't* match */
+#define RULE_IPV4	0x40	/* IPv4 only */
+#define RULE_IPV6	0x80	/* IPv6 only */
 
 struct rule {
     struct rule *next;
@@ -223,6 +225,12 @@
         case '~':
             r->rule_flags |= RULE_INVERSE;
             break;
+	case '4':
+	    r->rule_flags |= RULE_IPV4;
+	    break;
+	case '6':
+	    r->rule_flags |= RULE_IPV6;
+	    break;
 	case 'G':
 	case 'P':
             r->rule_mode = *p;
@@ -326,7 +334,7 @@
 
 /* Execute a rule set on a string; returns a malloc'd new string. */
 char *rewrite_string(const char *input, const struct rule *rules,
-                     char mode, match_pattern_callback macrosub,
+                     char mode, int af, match_pattern_callback macrosub,
                      const char **errmsg)
 {
     char *current = tfstrdup(input);
@@ -348,6 +356,12 @@
 	if (ruleptr->rule_mode && ruleptr->rule_mode != mode)
             continue;           /* Rule not applicable, try next */
 
+	if ((ruleptr->rule_flags & RULE_IPV4) && (af != AF_INET))
+            continue;           /* Rule not applicable, try next */
+
+	if ((ruleptr->rule_flags & RULE_IPV6) && (af != AF_INET6))
+            continue;           /* Rule not applicable, try next */
+
         if (!deadman--) {
             syslog(LOG_WARNING,
                    "remap: Breaking loop, input = %s, last = %s", input,
diff --git a/tftpd/remap.h b/tftpd/remap.h
index 69ca08d..7efcf6a 100644
--- a/tftpd/remap.h
+++ b/tftpd/remap.h
@@ -35,7 +35,7 @@
 void freerules(struct rule *);
 
 /* Execute a rule set on a string; returns a malloc'd new string. */
-char *rewrite_string(const char *, const struct rule *, char,
+char *rewrite_string(const char *, const struct rule *, char, int,
                      match_pattern_callback, const char **);
 
 #endif                          /* WITH_REGEX */
diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c
index 1873e70..88d2812 100644
--- a/tftpd/tftpd.c
+++ b/tftpd/tftpd.c
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 1983 Regents of the University of California.
  * Copyright (c) 1999-2009 H. Peter Anvin
- * Copyright (c) 2011 Intel Corporation; author: H. Peter Anvin
+ * Copyright (c) 2011-2014 Intel Corporation; author: H. Peter Anvin
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -1039,18 +1039,18 @@
     tp = (struct tftphdr *)buf;
     tp_opcode = ntohs(tp->th_opcode);
     if (tp_opcode == RRQ || tp_opcode == WRQ)
-        tftp(tp, n);
+	tftp(tp, n);
     exit(0);
 }
 
-static char *rewrite_access(char *, int, const char **);
+static char *rewrite_access(char *, int, int, const char **);
 static int validate_access(char *, int, const struct formats *, const char **);
 static void tftp_sendfile(const struct formats *, struct tftphdr *, int);
 static void tftp_recvfile(const struct formats *, struct tftphdr *, int);
 
 struct formats {
     const char *f_mode;
-    char *(*f_rewrite) (char *, int, const char **);
+    char *(*f_rewrite) (char *, int, int, const char **);
     int (*f_validate) (char *, int, const struct formats *, const char **);
     void (*f_send) (const struct formats *, struct tftphdr *, int);
     void (*f_recv) (const struct formats *, struct tftphdr *, int);
@@ -1112,9 +1112,8 @@
                 nak(EBADOP, "Unknown mode");
                 exit(0);
             }
-            if (!(filename =
-                  (*pf->f_rewrite) (origfilename, tp_opcode,
-                                    &errmsgptr))) {
+            if (!(filename = (*pf->f_rewrite)
+		  (origfilename, tp_opcode, from.sa.sa_family, &errmsgptr))) {
                 nak(EACCESS, errmsgptr);        /* File denied by mapping rule */
                 exit(0);
             }
@@ -1398,12 +1397,13 @@
 /*
  * Modify the filename, if applicable.  If it returns NULL, deny the access.
  */
-static char *rewrite_access(char *filename, int mode, const char **msg)
+static char *rewrite_access(char *filename, int mode, int af,
+			     const char **msg)
 {
     if (rewrite_rules) {
         char *newname =
             rewrite_string(filename, rewrite_rules,
-			   mode != RRQ ? 'P' : 'G',
+			   mode != RRQ ? 'P' : 'G', af,
                            rewrite_macros, msg);
         filename = newname;
     }
@@ -1411,10 +1411,11 @@
 }
 
 #else
-static char *rewrite_access(char *filename, int mode, const char **msg)
+static char *rewrite_access(char *filename, int mode, int af, const char **msg)
 {
     (void)mode;                 /* Avoid warning */
     (void)msg;
+    (void)af;
     return filename;
 }
 #endif