diff --git a/FPEngine.cc b/FPEngine.cc index ffa58d086..14423044f 100644 --- a/FPEngine.cc +++ b/FPEngine.cc @@ -118,7 +118,7 @@ FPNetworkControl::~FPNetworkControl() { /* (Re)-Initialize object's state (default parameter setup and nsock * initialization). */ -void FPNetworkControl::init(const char *ifname, devtype iftype) { +void FPNetworkControl::init(const char *ifname) { /* Init congestion control parameters */ this->cc_init(); @@ -155,27 +155,14 @@ void FPNetworkControl::init(const char *ifname, devtype iftype) { /* Flag it as already initialized so we free this nsp next time */ this->nsock_init = true; + /* We don't need to store the eth handle because FPProbes come with a + * suitable one (FPProbes::getEthernet()), we just attempt to obtain one + * to see if it fails. */ + netutil_eth_t *ethsd = NULL; + /* Obtain raw socket or check that we can obtain an eth descriptor. */ - if ((o.sendpref & PACKET_SEND_ETH) && (iftype == devt_ethernet -#ifdef WIN32 - || (o.have_pcap && iftype == devt_loopback) -#endif - ) && ifname != NULL) { - /* We don't need to store the eth handler because FPProbes come with a - * suitable one (FPProbes::getEthernet()), we just attempt to obtain one - * to see if it fails. */ - if (eth_open_cached(ifname) == NULL) - fatal("dnet: failed to open device %s", ifname); - this->rawsd = -1; - } else { -#ifdef WIN32 - win32_fatal_raw_sockets(ifname); -#endif - if (this->rawsd >= 0) - close(this->rawsd); - rawsd = nmap_raw_socket(); - if (rawsd < 0) - pfatal("Couldn't obtain raw socket in %s", __func__); + if (!raw_socket_or_eth(o.sendpref, ifname, &this->rawsd, ðsd)) { + fatal("Couldn't obtain raw socket or eth handle in %s", __func__); } /* De-register existing callers */ @@ -1135,7 +1122,7 @@ int FPEngine6::os_scan(std::vector &Targets) { /* Initialize variables, timers, etc. */ gettimeofday(&begin_time, NULL); - global_netctl.init(Targets[0]->deviceName(), Targets[0]->ifType()); + global_netctl.init(Targets[0]->deviceName()); for (size_t i = 0; i < Targets.size(); i++) { if (o.debugging > 3) { log_write(LOG_PLAIN, "[FPEngine] Allocating FPHost6 for %s %s\n", diff --git a/FPEngine.h b/FPEngine.h index e489534d9..4dd6c2bd2 100644 --- a/FPEngine.h +++ b/FPEngine.h @@ -157,7 +157,7 @@ class FPNetworkControl { public: FPNetworkControl(); ~FPNetworkControl(); - void init(const char *ifname, devtype iftype); + void init(const char *ifname); int register_caller(FPHost *newcaller); int unregister_caller(FPHost *oldcaller); int setup_sniffer(const char *iface, const char *bfp_filter); diff --git a/NmapOps.cc b/NmapOps.cc index e547fcd76..aa9bfa660 100644 --- a/NmapOps.cc +++ b/NmapOps.cc @@ -68,6 +68,7 @@ #include "NmapOps.h" #include "osscan.h" #include "nmap_error.h" +#include "libnetutil/netutil.h" NmapOps o; diff --git a/NmapOps.h b/NmapOps.h index d2de5a35b..283612bb9 100644 --- a/NmapOps.h +++ b/NmapOps.h @@ -123,24 +123,6 @@ class NmapOps { u8 debugging; bool resuming; -#define PACKET_SEND_NOPREF 1 -#define PACKET_SEND_ETH_WEAK 2 -#define PACKET_SEND_ETH_STRONG 4 -#define PACKET_SEND_ETH 6 -#define PACKET_SEND_IP_WEAK 8 -#define PACKET_SEND_IP_STRONG 16 -#define PACKET_SEND_IP 24 - - /* How should we send raw IP packets? Nmap can generally use either - ethernet or raw ip sockets. Which is better depends on platform - and goals. A _STRONG preference means that Nmap should use the - preferred method whenever it is possible (obviously it isn't - always possible -- sending ethernet frames won't work over a PPP - connection). This is useful when the other type doesn't work at - all. A _WEAK preference means that Nmap may use the other type - where it is substantially more efficient to do so. For example, - Nmap will still do an ARP ping scan of a local network even when - the pref is SEND_IP_WEAK */ int sendpref; bool packetTrace() { return (debugging >= 3)? true : pTrace; } bool versionTrace() { return packetTrace()? true : vTrace; } diff --git a/idle_scan.cc b/idle_scan.cc index dd913b2c6..14139c887 100644 --- a/idle_scan.cc +++ b/idle_scan.cc @@ -599,30 +599,19 @@ static void initialize_idleproxy(struct idle_proxy_info *proxy, char *proxyName, /* Now lets send some probes to check IP ID algorithm ... */ /* First we need a raw socket ... */ - if ((o.sendpref & PACKET_SEND_ETH) && (proxy->host.ifType() == devt_ethernet -#ifdef WIN32 - || (o.have_pcap && proxy->host.ifType() == devt_loopback) -#endif - )) { + if (!raw_socket_or_eth(o.sendpref, proxy->host.deviceName(), + &proxy->rawsd, &proxy->eth.ethsd)) { + fatal("%s: Failed to open raw socket or ethernet handle", __func__); + } + if (proxy->eth.ethsd != NULL) { if (!setTargetNextHopMAC(&proxy->host)) fatal("%s: Failed to determine dst MAC address for Idle proxy", __func__); memcpy(proxy->eth.srcmac, proxy->host.SrcMACAddress(), 6); memcpy(proxy->eth.dstmac, proxy->host.NextHopMACAddress(), 6); - proxy->eth.ethsd = eth_open_cached(proxy->host.deviceName()); - if (proxy->eth.ethsd == NULL) - fatal("%s: Failed to open ethernet device (%s)", __func__, proxy->host.deviceName()); - proxy->rawsd = -1; proxy->ethptr = &proxy->eth; - } else { -#ifdef WIN32 - win32_fatal_raw_sockets(proxy->host.deviceName()); -#endif - proxy->rawsd = nmap_raw_socket(); - if (proxy->rawsd < 0) - pfatal("socket troubles in %s", __func__); + } + else { unblock_socket(proxy->rawsd); - proxy->eth.ethsd = NULL; - proxy->ethptr = NULL; } if (proxy->host.af() == AF_INET6) diff --git a/libnetutil/netutil.cc b/libnetutil/netutil.cc index 5bd0237b2..c0e54f455 100644 --- a/libnetutil/netutil.cc +++ b/libnetutil/netutil.cc @@ -1088,6 +1088,98 @@ void eth_close_cached() { return; } +static void netutil_perror(const char *msg) { + int err = socket_errno(); + netutil_error("%s: (%d) %s", msg, err, socket_strerror(err)); +} + +/* Create a raw socket and do things that always apply to raw sockets: + * Set SO_BROADCAST. + * Set IP_HDRINCL. + * Bind to an interface with SO_BINDTODEVICE (if device is not NULL). + The socket is created with address family AF_INET, but may be usable for + AF_INET6, depending on the operating system. */ +int netutil_raw_socket(const char *device) { +#ifdef WIN32 + netutil_error("Windows does not have adequate raw socket support."); + return -1; +#else + int rawsd; + int one = 1; + + rawsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (rawsd < 0) { + netutil_perror("Couldn't open a raw socket. " +#if defined(sun) && defined(__SVR4) + "In Solaris shared-IP non-global zones, this requires the PRIV_NET_RAWACCESS privilege. " +#endif + "Error"); + return -1; + } + if (setsockopt (rawsd, SOL_SOCKET, SO_BROADCAST, (const char *) &one, sizeof(int)) != 0) { + netutil_perror("setsockopt(SO_BROADCAST) failed"); + } + sethdrinclude(rawsd); + socket_bindtodevice(rawsd, device); + + return rawsd; +#endif +} + +int raw_socket_or_eth(int sendpref, const char *ifname, + int *rawsd, netutil_eth_t **ethsd) { + assert(rawsd != NULL); + *rawsd = -1; + assert(ethsd != NULL); + *ethsd = NULL; + bool may_try_eth = ifname && !(sendpref & PACKET_SEND_IP_STRONG); + bool may_try_ip = !(sendpref & PACKET_SEND_ETH_STRONG); + bool try_eth = may_try_eth && (sendpref & PACKET_SEND_ETH); + bool try_ip = may_try_ip && (sendpref & PACKET_SEND_IP); + + for (int i = 0; i < 2; i++) { + if (try_eth) { + try_eth = false; + may_try_eth = false; + try_ip = may_try_ip; + + netutil_eth_t *e = eth_open_cached(ifname); + *ethsd = e; + if (e == NULL) { + netutil_error("dnet: failed to open device %s", ifname); + } + else if (!netutil_eth_can_send(e)) { + netutil_error("%s is not a supported device for sending.", ifname); + e = NULL; + } + else { + break; + } + } + + if (try_ip) { + try_ip = false; + may_try_ip = false; + try_eth = may_try_eth; + +#ifdef WIN32 + /* For Windows, don't even bother trying unless the caller insists, to + * avoid excessive error messages. */ + if (!(sendpref & PACKET_SEND_IP_STRONG)) { + continue; + } +#endif + int sd = netutil_raw_socket(ifname); + *rawsd = sd; + if (sd >= 0) { + break; + } + } + } + + return (*ethsd != NULL || *rawsd >= 0) ? 1 : 0; +} + /* Takes a protocol number like IPPROTO_TCP, IPPROTO_UDP, IPPROTO_IP, * etc, and returns an ASCII representation (or the string "unknown" if * it doesn't recognize the number). If uppercase is non zero, the diff --git a/libnetutil/netutil.h b/libnetutil/netutil.h index 8cf9c3646..3816570d1 100644 --- a/libnetutil/netutil.h +++ b/libnetutil/netutil.h @@ -305,6 +305,34 @@ int netutil_eth_can_send(const netutil_eth_t *e); /* See the description for eth_open_cached */ void eth_close_cached(); +/* Create a raw socket and do things that always apply to raw sockets: + * Set SO_BROADCAST. + * Set IP_HDRINCL. + * Bind to an interface with SO_BINDTODEVICE (if device is not NULL). + The socket is created with address family AF_INET, but may be usable for + AF_INET6, depending on the operating system. */ +int netutil_raw_socket(const char *device); + +/* How should we send raw IP packets? Nmap can generally use either + ethernet or raw ip sockets. Which is better depends on platform + and goals. A _STRONG preference means that Nmap should use the + preferred method whenever it is possible (obviously it isn't + always possible -- sending ethernet frames won't work over a PPP + connection). This is useful when the other type doesn't work at + all. A _WEAK preference means that Nmap may use the other type + where it is substantially more efficient to do so. For example, + Nmap will still do an ARP ping scan of a local network even when + the pref is SEND_IP_WEAK */ +#define PACKET_SEND_NOPREF 0x01 +#define PACKET_SEND_ETH_WEAK 0x02 +#define PACKET_SEND_ETH_STRONG 0x04 +#define PACKET_SEND_ETH (PACKET_SEND_ETH_WEAK | PACKET_SEND_ETH_STRONG) +#define PACKET_SEND_IP_WEAK 0x08 +#define PACKET_SEND_IP_STRONG 0x10 +#define PACKET_SEND_IP (PACKET_SEND_IP_WEAK | PACKET_SEND_IP_STRONG) +int raw_socket_or_eth(int sendpref, const char *ifname, + int *rawsd, netutil_eth_t **ethsd); + /* Takes a protocol number like IPPROTO_TCP, IPPROTO_UDP, or * IPPROTO_IP and returns a ascii representation (or "unknown" if it * doesn't recognize the number). Returned string is in lowercase. */ diff --git a/nping/NpingOps.cc b/nping/NpingOps.cc index 5b13c9554..0a1988dae 100644 --- a/nping/NpingOps.cc +++ b/nping/NpingOps.cc @@ -105,7 +105,6 @@ NpingOps::NpingOps() { sendpref_set=false; send_eth=false; - send_eth_set=false; delay=0; delay_set=false; @@ -620,10 +619,10 @@ bool NpingOps::issetPacketCount(){ * PACKET_SEND_ETH, PACKET_SEND_IP_WEAK, PACKET_SEND_IP_STRONG, PACKET_SEND_IP * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ int NpingOps::setSendPreference(int v){ - if( v!=PACKET_SEND_NOPREF && v!=PACKET_SEND_ETH_WEAK && - v!=PACKET_SEND_ETH_STRONG && v!=PACKET_SEND_ETH && - v!=PACKET_SEND_IP_WEAK && v!=PACKET_SEND_IP_STRONG && - v!=PACKET_SEND_IP ){ + // Validate: no extra bits set + if( (v & ~(PACKET_SEND_ETH | PACKET_SEND_IP)) + // Validate: both ETH and IP cannot be STRONG + || ((v & PACKET_SEND_ETH_STRONG) && (v & PACKET_SEND_IP_STRONG))) { nping_fatal(QT_3,"setSendPreference(): Invalid value supplied\n"); return OP_FAILURE; }else{ @@ -648,51 +647,31 @@ bool NpingOps::issetSendPreference(){ /* Returns true if send preference is Ethernet */ bool NpingOps::sendPreferenceEthernet(){ - if ( this->getSendPreference()==PACKET_SEND_ETH_WEAK ) - return true; - else if (this->getSendPreference()==PACKET_SEND_ETH_STRONG) - return true; - else if (this->getSendPreference()==PACKET_SEND_ETH ) - return true; - else - return false; + return (this->sendpref & PACKET_SEND_ETH); } /* End of sendPreferenceEthernet() */ /* Returns true if send preference is Ethernet */ bool NpingOps::sendPreferenceIP(){ - if ( this->getSendPreference()==PACKET_SEND_IP_WEAK ) - return true; - else if (this->getSendPreference()==PACKET_SEND_IP_STRONG) - return true; - else if (this->getSendPreference()==PACKET_SEND_IP ) - return true; - else - return false; + return (this->sendpref & PACKET_SEND_IP); } /* End of sendPreferenceIP() */ /** Sets SendEth. * @return OP_SUCCESS on success and OP_FAILURE in case of error. */ int NpingOps::setSendEth(bool val){ - this->send_eth=val; - this->send_eth_set=true; + this->sendpref = PACKET_SEND_ETH; + this->sendpref_set = true; return OP_SUCCESS; } /* End of setSendEth() */ /** Returns value of attribute send_eth */ bool NpingOps::sendEth(){ - return this->send_eth; + return (this->sendpref & PACKET_SEND_ETH_STRONG); } /* End of getSendEth() */ -/* Returns true if option has been set */ -bool NpingOps::issetSendEth(){ - return this->send_eth_set; -} /* End of issetSendEth() */ - - /** Sets inter-probe delay. Supplied parameter is assumed to be in milliseconds * and must be a long integer greater than zero. * @warning timeout is assumed to be in milliseconds. Use tval2msecs() from diff --git a/nping/NpingOps.h b/nping/NpingOps.h index 15df7b6d6..f9ce6440b 100644 --- a/nping/NpingOps.h +++ b/nping/NpingOps.h @@ -97,14 +97,6 @@ #define FLAG_SYN 6 #define FLAG_FIN 7 -#define PACKET_SEND_NOPREF 1 /* These have been taken from NmapOps.h */ -#define PACKET_SEND_ETH_WEAK 2 -#define PACKET_SEND_ETH_STRONG 4 -#define PACKET_SEND_ETH 6 -#define PACKET_SEND_IP_WEAK 8 -#define PACKET_SEND_IP_STRONG 16 -#define PACKET_SEND_IP 24 - #define IP_VERSION_4 0x04 #define IP_VERSION_6 0x06 diff --git a/nse_dnet.cc b/nse_dnet.cc index 41e3f4711..0686319e4 100644 --- a/nse_dnet.cc +++ b/nse_dnet.cc @@ -223,7 +223,7 @@ static int ethernet_send (lua_State *L) static int ip_open (lua_State *L) { nse_dnet_udata *udata = (nse_dnet_udata *) nseU_checkudata(L, 1, DNET_METATABLE, "dnet"); - udata->sock = nmap_raw_socket(); + udata->sock = netutil_raw_socket(NULL); if (udata->sock == -1) { if (o.scriptTrace()) { diff --git a/osscan2.cc b/osscan2.cc index af44699f2..292a76184 100644 --- a/osscan2.cc +++ b/osscan2.cc @@ -1336,24 +1336,11 @@ HostOsScan::HostOsScan(Target *t) { rawsd = -1; ethsd = NULL; - if ((o.sendpref & PACKET_SEND_ETH) && (t->ifType() == devt_ethernet -#ifdef WIN32 - || (o.have_pcap && t->ifType() == devt_loopback) -#endif - )) { - if ((ethsd = eth_open_cached(t->deviceName())) == NULL) - fatal("%s: Failed to open ethernet device (%s)", __func__, t->deviceName()); - rawsd = -1; - } else { -#ifdef WIN32 - win32_fatal_raw_sockets(t->deviceName()); -#endif - rawsd = nmap_raw_socket(); - if (rawsd < 0) - pfatal("socket troubles in %s", __func__); - unblock_socket(rawsd); - ethsd = NULL; + if (!raw_socket_or_eth(o.sendpref, t->deviceName(), &rawsd, ðsd)) { + fatal("%s: Failed to open raw socket or ethernet device", __func__); } + if (rawsd >= 0) + unblock_socket(rawsd); if (o.magic_port_set) { tcpPortBase = o.magic_port; diff --git a/output.cc b/output.cc index 4ee97b364..df8238df0 100644 --- a/output.cc +++ b/output.cc @@ -218,22 +218,6 @@ static void print_xml_service(const struct serviceDeductions *sd) { } #ifdef WIN32 -/* Show a fatal error explaining that an interface is not Ethernet and won't - work on Windows. Do nothing if --send-ip (PACKET_SEND_IP_STRONG) was used. */ -void win32_fatal_raw_sockets(const char *devname) { - if ((o.sendpref & PACKET_SEND_IP_STRONG) != 0) - return; - - if (devname != NULL) { - fatal("Only ethernet devices can be used for raw scans on Windows, and\n" - "\"%s\" is not an ethernet device. Use the --unprivileged option\n" - "for this scan.", devname); - } else { - fatal("Only ethernet devices can be used for raw scans on Windows. Use\n" - "the --unprivileged option for this scan."); - } -} - /* Display the mapping from libdnet interface names (like "eth0") to Npcap interface names (like "\Device\NPF_{...}"). This is the same mapping used by eth_open and so can help diagnose connection problems. Additionally display diff --git a/output.h b/output.h index f6309d3d0..f4141cfd4 100644 --- a/output.h +++ b/output.h @@ -123,12 +123,6 @@ class Target; # endif #endif -#ifdef WIN32 -/* Show a fatal error explaining that an interface is not Ethernet and won't - work on Windows. Do nothing if --send-ip (PACKET_SEND_IP_STRONG) was used. */ -void win32_fatal_raw_sockets(const char *devname); -#endif - /* Prints the familiar Nmap tabular output showing the "interesting" ports found on the machine. It also handles the Machine/Grepable output and the XML output. It is pretty ugly -- in particular I diff --git a/scan_engine.cc b/scan_engine.cc index a3eaf0b05..3f96e51d7 100644 --- a/scan_engine.cc +++ b/scan_engine.cc @@ -949,39 +949,15 @@ void UltraScanInfo::Init(std::vector &Targets, const struct scan_lists aren't doing a TCP connect scan, or if we're doing a ping scan that requires it. */ if (isRawScan()) { - const char *device = Targets[0]->deviceName(); - if (ping_scan_arp || (ping_scan_nd && o.sendpref != PACKET_SEND_IP_STRONG) || (o.sendpref & PACKET_SEND_ETH)) { - /* We'll send ethernet packets with dnet */ - ethsd = eth_open_cached(device); - if (ethsd == NULL) { - error("dnet: Failed to open device %s", device); - } - else if (!netutil_eth_can_send(ethsd)) { - ethsd = NULL; - } - /* If eth failed, we can fall back to raw socket. The only exception is - * ARP ping, which needs Ethernet link. */ - if (ping_scan_arp && (ethsd == NULL || netutil_eth_datalink(ethsd) != DLT_EN10MB)) { - fatal("ARP ping not supported on %s", device); - } + /* If eth failed, we can fall back to raw socket. The only exception is + * ARP ping, which needs Ethernet link. */ + int sendpref = o.sendpref; + if (ping_scan_arp) { + assert(!(sendpref & PACKET_SEND_IP_STRONG)); + sendpref = PACKET_SEND_ETH; } - if (ethsd == NULL) { -#ifdef WIN32 - win32_fatal_raw_sockets(Targets[0]->deviceName()); -#endif - rawsd = nmap_raw_socket(); - if (rawsd < 0) - pfatal("Couldn't open a raw socket. " -#if defined(sun) && defined(__SVR4) - "In Solaris shared-IP non-global zones, this requires the PRIV_NET_RAWACCESS privilege. " -#endif - "Error" - ); - /* We do not want to unblock the socket since we want to wait - if kernel send buffers fill up rather than get ENOBUF, and - we won't be receiving on the socket anyway - unblock_socket(rawsd);*/ - ethsd = NULL; + if (!raw_socket_or_eth(sendpref, Targets[0]->deviceName(), &rawsd, ðsd)) { + fatal("Couldn't open a raw socket or eth handle."); } /* Raw scan types also need to know the source IP. */ Targets[0]->SourceSockAddr(&sourceSockAddr, NULL); diff --git a/tcpip.cc b/tcpip.cc index e38d20c15..a82af6234 100644 --- a/tcpip.cc +++ b/tcpip.cc @@ -87,31 +87,6 @@ extern NmapOps o; static PacketCounter PktCt; -/* Create a raw socket and do things that always apply to raw sockets: - * Set SO_BROADCAST. - * Set IP_HDRINCL. - * Bind to an interface with SO_BINDTODEVICE (if o.device is set). - The socket is created with address family AF_INET, but may be usable for - AF_INET6, depending on the operating system. */ -int nmap_raw_socket() { - int rawsd; - int one = 1; - - rawsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); - if (rawsd < 0) - return rawsd; - if (setsockopt (rawsd, SOL_SOCKET, SO_BROADCAST, (const char *) &one, sizeof(int)) != 0) { - error("Failed to secure socket broadcasting permission"); - perror("setsockopt"); - } -#ifndef WIN32 - sethdrinclude(rawsd); -#endif - socket_bindtodevice(rawsd, o.device); - - return rawsd; -} - /* Fill buf (up to buflen -- truncate if necessary but always terminate) with a short representation of the packet stats. Returns buf. Aborts if there is a problem. */ diff --git a/traceroute.cc b/traceroute.cc index dc9f40483..affaa6663 100644 --- a/traceroute.cc +++ b/traceroute.cc @@ -837,19 +837,8 @@ TracerouteState::TracerouteState(std::vector &targets) { assert(targets.size() > 0); - if ((o.sendpref & PACKET_SEND_ETH) && targets[0]->ifType() == devt_ethernet) { - ethsd = eth_open_cached(targets[0]->deviceName()); - if (ethsd == NULL) - fatal("dnet: failed to open device %s", targets[0]->deviceName()); - rawsd = -1; - } else { -#ifdef WIN32 - win32_fatal_raw_sockets(targets[0]->deviceName()); -#endif - rawsd = nmap_raw_socket(); - if (rawsd < 0) - pfatal("traceroute: socket troubles"); - ethsd = NULL; + if (!raw_socket_or_eth(o.sendpref, targets[0]->deviceName(), &rawsd, ðsd)) { + fatal("traceroute: socket troubles"); } /* Assume that all the targets share the same device. */