@@ -1067,8 +1067,10 @@ static void accept_client(int fd, short revents, void *arg)
10671067static void usage (const char * name )
10681068{
10691069 fprintf (stderr , "%s, ODoH proxy with legacy RFC8484 forwarding.\n" , name );
1070- fprintf (stderr , "Usage: %s -c cert -k key [-p port] [-u user] [-A cafile] [--target-url https://host/path]... [--targets-file file] [--target-cert cert --target-key key] [-F] [-v] [-V] [-h]\n" , name );
1070+ fprintf (stderr , "Usage: %s -c cert -k key [-p port] [-4|-6] [- u user] [-A cafile] [--target-url https://host/path]... [--targets-file file] [--target-cert cert --target-key key] [-F] [-v] [-V] [-h]\n" , name );
10711071 fprintf (stderr , " cert/key: TLS server certificate and key\n" );
1072+ fprintf (stderr , " -4: force IPv4 only\n" );
1073+ fprintf (stderr , " -6: force IPv6 only (default: dual-stack)\n" );
10721074 fprintf (stderr , " -A/--ca-file: CA bundle for verifying upstream target TLS certs\n" );
10731075 fprintf (stderr , " --target-url: repeatable legacy RFC8484 target URL\n" );
10741076 fprintf (stderr , " --targets-file: file with target URLs (one per line)\n" );
@@ -1083,11 +1085,13 @@ int main(int argc, char *argv[])
10831085 char * targets_file = NULL ;
10841086 char * upstream_cafile = NULL ;
10851087 uint16_t port = PROXY_PORT ;
1088+ int ip_version = 0 ; /* 0 = dual-stack, 4 = IPv4 only, 6 = IPv6 only */
10861089 int foreground = 0 ;
10871090 int loglvl = DOH_WARN ;
10881091 int c , option_idx = 0 ;
10891092 int yes = 1 ;
1090- struct sockaddr_in6 addr ;
1093+ struct sockaddr_in6 addr6 ;
1094+ struct sockaddr_in addr4 ;
10911095 struct option long_options [] = {
10921096 {"help" , 0 , 0 , 'h' },
10931097 {"version" , 0 , 0 , 'V' },
@@ -1102,11 +1106,13 @@ int main(int argc, char *argv[])
11021106 {"target-key" , 1 , 0 , 'y' },
11031107 {"target-url" , 1 , 0 , 't' },
11041108 {"targets-file" , 1 , 0 , 'T' },
1109+ {"ipv4" , 0 , 0 , '4' },
1110+ {"ipv6" , 0 , 0 , '6' },
11051111 {NULL , 0 , 0 , 0 }
11061112 };
11071113
11081114 while (1 ) {
1109- c = getopt_long (argc , argv , "hVc :k:p:u:A:vFx:y:t:T:" , long_options , & option_idx );
1115+ c = getopt_long (argc , argv , "46hVc :k:p:u:A:vFx:y:t:T:" , long_options , & option_idx );
11101116 if (c < 0 )
11111117 break ;
11121118
@@ -1122,6 +1128,8 @@ int main(int argc, char *argv[])
11221128 case 'F' : foreground = 1 ; break ;
11231129 case 'x' : target_client_cert = strdup (optarg ); break ;
11241130 case 'y' : target_client_key = strdup (optarg ); break ;
1131+ case '4' : ip_version = 4 ; break ;
1132+ case '6' : ip_version = 6 ; break ;
11251133 case 't' :
11261134 if (add_target_url (optarg ) != 0 )
11271135 return 2 ;
@@ -1181,19 +1189,50 @@ int main(int argc, char *argv[])
11811189 if (wolfSSL_CTX_use_PrivateKey_file (srv_ctx , key , SSL_FILETYPE_PEM ) != SSL_SUCCESS )
11821190 return 1 ;
11831191
1184- lfd = socket (AF_INET6 , SOCK_STREAM , 0 );
1185- if (lfd < 0 )
1186- return 1 ;
1192+ /* Create listening socket based on IP version preference */
1193+ if (ip_version == 4 ) {
1194+ /* IPv4 only */
1195+ lfd = socket (AF_INET , SOCK_STREAM , 0 );
1196+ if (lfd < 0 )
1197+ return 1 ;
1198+ setsockopt (lfd , SOL_SOCKET , SO_REUSEADDR , (char * )& yes , sizeof (yes ));
1199+ setsockopt (lfd , SOL_SOCKET , SO_REUSEPORT , (char * )& yes , sizeof (yes ));
11871200
1188- setsockopt (lfd , SOL_SOCKET , SO_REUSEADDR , (char * )& yes , sizeof (yes ));
1189- setsockopt (lfd , SOL_SOCKET , SO_REUSEPORT , (char * )& yes , sizeof (yes ));
1201+ memset (& addr4 , 0 , sizeof (addr4 ));
1202+ addr4 .sin_family = AF_INET ;
1203+ addr4 .sin_addr .s_addr = INADDR_ANY ;
1204+ addr4 .sin_port = htons (port );
11901205
1191- memset (& addr , 0 , sizeof (addr ));
1192- addr .sin6_family = AF_INET6 ;
1193- addr .sin6_port = htons (port );
1206+ if (bind (lfd , (struct sockaddr * )& addr4 , sizeof (addr4 )) != 0 )
1207+ return 1 ;
1208+ dohprint (DOH_NOTICE , "dohproxyd listening on 0.0.0.0:%u (IPv4 only)" , port );
1209+ } else {
1210+ /* IPv6 (with or without dual-stack) */
1211+ lfd = socket (AF_INET6 , SOCK_STREAM , 0 );
1212+ if (lfd < 0 )
1213+ return 1 ;
1214+ setsockopt (lfd , SOL_SOCKET , SO_REUSEADDR , (char * )& yes , sizeof (yes ));
1215+ setsockopt (lfd , SOL_SOCKET , SO_REUSEPORT , (char * )& yes , sizeof (yes ));
11941216
1195- if (bind (lfd , (struct sockaddr * )& addr , sizeof (addr )) != 0 )
1196- return 1 ;
1217+ if (ip_version == 6 ) {
1218+ /* IPv6 only - disable dual-stack */
1219+ int ipv6only = 1 ;
1220+ setsockopt (lfd , IPPROTO_IPV6 , IPV6_V6ONLY , & ipv6only , sizeof (ipv6only ));
1221+ }
1222+
1223+ memset (& addr6 , 0 , sizeof (addr6 ));
1224+ addr6 .sin6_family = AF_INET6 ;
1225+ addr6 .sin6_addr = in6addr_any ;
1226+ addr6 .sin6_port = htons (port );
1227+
1228+ if (bind (lfd , (struct sockaddr * )& addr6 , sizeof (addr6 )) != 0 )
1229+ return 1 ;
1230+
1231+ if (ip_version == 6 )
1232+ dohprint (DOH_NOTICE , "dohproxyd listening on [::]:%u (IPv6 only)" , port );
1233+ else
1234+ dohprint (DOH_NOTICE , "dohproxyd listening on [::]:%u (dual-stack)" , port );
1235+ }
11971236
11981237 if (listen (lfd , 32 ) != 0 )
11991238 return 1 ;
@@ -1207,7 +1246,6 @@ int main(int argc, char *argv[])
12071246 }
12081247
12091248 evquick_addevent (lfd , EVQUICK_EV_READ , accept_client , NULL , NULL );
1210- dohprint (DOH_NOTICE , "dohproxyd listening on [::]:%u" , port );
12111249
12121250 while (run )
12131251 evquick_loop ();
0 commit comments