--- parser3/src/classes/inet.C 2008/06/16 13:09:22 1.2 +++ parser3/src/classes/inet.C 2015/10/12 21:34:36 1.10 @@ -1,21 +1,24 @@ /** @file Parser: @b inet parser class. - Copyright(c) 2001-2005 ArtLebedev Group(http://www.artlebedev.com) + Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) */ -static const char * const IDENT_INET_C="$Date: 2008/06/16 13:09:22 $"; - #include "pa_vmethod_frame.h" #include "pa_request.h" +#include "pa_vtable.h" + +#ifdef _MSC_VER +#include "winsock2.h" +#include "ws2tcpip.h" +#endif + +volatile const char * IDENT_INET_C="$Id: inet.C,v 1.10 2015/10/12 21:34:36 moko Exp $"; class MInet: public Methoded { public: MInet(); - -public: // Methoded - bool used_directly() { return true; } }; // global variables @@ -25,24 +28,16 @@ DECLARE_CLASS_VAR(inet, new MInet, 0); static void _ntoa(Request& r, MethodParams& params){ unsigned long l=(unsigned long)trunc(params.as_double(0, "parameter must be expression", r)); - static const int ip_cstr_bufsize=3*4+3+1+1; + static const int ip_cstr_bufsize=3*4+3+1/*zero-teminator*/+1/*for faulty snprintfs*/; char* ip_cstr=new(PointerFreeGC) char[ip_cstr_bufsize]; - - snprintf(ip_cstr, ip_cstr_bufsize, "%u.%u.%u.%u", - (l>>24) & 0xFF, - (l>>16) & 0xFF, - (l>>8) & 0xFF, - l & 0xFF); - + snprintf(ip_cstr, ip_cstr_bufsize, "%u.%u.%u.%u", (l>>24) & 0xFF, (l>>16) & 0xFF, (l>>8) & 0xFF, l & 0xFF); r.write_no_lang(*new String(ip_cstr)); } static void _aton(Request& r, MethodParams& params){ const String ip=params.as_string(0, PARAMETER_MUST_BE_STRING); if(ip.is_empty()) - throw Exception(PARSER_RUNTIME, - 0, - "IP address must not be empty."); + throw Exception(PARSER_RUNTIME, 0, "IP address must not be empty."); const char* ip_cstr=ip.cstr(); ulong result=0; @@ -52,10 +47,10 @@ static void _aton(Request& r, MethodPara bool err=false; const char* p=ip_cstr; while(char c=*p++){ - uint digit=(uint)(c-'0'); // assume ascii + int digit=(int)(c-'0'); // assume ascii if(digit>=0 && digit<=9){ byte_start=false; - if((byte_value=byte_value*10+digit) > 255){ + if((byte_value=byte_value*10+(uint)digit) > 255){ err=true; break; } @@ -76,19 +71,130 @@ static void _aton(Request& r, MethodPara } if(err || dot_cnt!=3 || byte_start){ - throw Exception(PARSER_RUNTIME, - 0, - "Invalid IP address '%s' specified.", ip_cstr); + throw Exception(PARSER_RUNTIME, 0, "Invalid IP address '%s' specified.", ip_cstr); } else { result=(result << 8)+(ulong)byte_value; r.write_no_lang(*new VDouble(result)); } } +int ipv_format(const String &value){ + if(value == "4") return AF_INET; + if(value == "6") return AF_INET6; + if(value == "any") return AF_UNSPEC; + throw Exception(PARSER_RUNTIME, &value, "ipv option value must be 4 or 6 or any"); +} + +static void _ip2name(Request& r, MethodParams& params){ + const String sip=params.as_string(0, PARAMETER_MUST_BE_STRING); + if(sip.is_empty()) + throw Exception(PARSER_RUNTIME, 0, "IP address must not be empty."); + + const char* ip_cstr=sip.cstr(); + + struct addrinfo hints, *info=0; + memset(&hints, 0, sizeof(hints)); + hints.ai_family=AF_INET; + hints.ai_socktype=SOCK_STREAM; + hints.ai_flags=AI_NUMERICHOST; // to disable DNS lookup + + if(params.count() == 2) + if(HashStringValue* options=params.as_hash(1)){ + int valid_options=0; + if(Value* value=options->get("ipv")){ + hints.ai_family=ipv_format(r.process_to_value(*value).as_string()); + valid_options++; + } + if(valid_options!=options->count()) + throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); + } + + int error=getaddrinfo(ip_cstr, 0, &hints, &info); + if(error==EAI_NONAME) + throw Exception(PARSER_RUNTIME, 0, "Invalid IP address '%s' specified", ip_cstr); + if(error) + throw Exception(PARSER_RUNTIME, 0, "Invalid IP address '%s': %s", ip_cstr, gai_strerror(error)); + + char hbuf[NI_MAXHOST]; + error=getnameinfo(info->ai_addr, info->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD); + + freeaddrinfo(info); + + if(!error){ + r.write_no_lang(*new String(pa_idna_decode(pa_strdup(hbuf), r.charsets.source()), String::L_TAINTED)); + } else if(error!=EAI_NONAME){ + throw Exception(PARSER_RUNTIME, 0, "Can't resolve IP address '%s': %s", ip_cstr, gai_strerror(error)); + } +} +static void _name2ip(Request& r, MethodParams& params){ + const String sname=params.as_string(0, PARAMETER_MUST_BE_STRING); + if(sname.is_empty()) + throw Exception(PARSER_RUNTIME, 0, "Domain name must not be empty."); + + const char* name_cstr=pa_idna_encode(sname.cstr(), r.charsets.source()); + + struct addrinfo hints, *info; + memset(&hints, 0, sizeof(hints)); + hints.ai_family=AF_INET; + hints.ai_socktype=SOCK_STREAM; + + Table *table=NULL; + + if(params.count() == 2) + if(HashStringValue* options=params.as_hash(1)){ + int valid_options=0; + if(Value* value=options->get("ipv")){ + hints.ai_family=ipv_format(r.process_to_value(*value).as_string()); + valid_options++; + } + if(Value* value=options->get("table")){ + if(r.process_to_value(*value).as_bool()){ + Table::columns_type columns(new ArrayString); + static const String sip("ip"), sversion("version"); + *columns+=&sip; *columns+=&sversion; + table=new Table(columns); + } + valid_options++; + } + if(valid_options!=options->count()) + throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION); + } + + int error=getaddrinfo(name_cstr, NULL, &hints, &info); + + if(error) + throw Exception(PARSER_RUNTIME, 0, "Can't resolve domain name '%s': %s", name_cstr, gai_strerror(error)); + + char hbuf[INET6_ADDRSTRLEN]; + + for(struct addrinfo *cur=info; cur; cur=cur->ai_next) { + if(error=getnameinfo(cur->ai_addr, cur->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) + throw Exception(PARSER_RUNTIME, 0, "Can't translate address: %s", gai_strerror(error)); + String *saddr=new String(pa_strdup(hbuf), String::L_TAINTED); + if(table){ + Table::element_type row(new ArrayString()); + static const String sv4("4"), sv6("6"), sunknown("unknown"); + *row+=saddr; + *row+=cur->ai_family == AF_INET ? &sv4 : cur->ai_family == AF_INET6 ? &sv6 : &sunknown; + *table+=row; + } else { + r.write_no_lang(*saddr); + break; + } + } + + if(table) + r.write_no_lang(*new VTable(table)); + + freeaddrinfo(info); +} + // constructor MInet::MInet(): Methoded("inet") { add_native_method("ntoa", Method::CT_STATIC, _ntoa, 1, 1); add_native_method("aton", Method::CT_STATIC, _aton, 1, 1); - - // todo: gethostbyname, gethostbyaddr + // ^inet:ip2name[ip; $.ipv[4|6|any] ] + add_native_method("ip2name", Method::CT_STATIC, _ip2name, 1, 2); + // ^inet:name2ip[name; $.ipv[4|6|any] $.table(true) ] + add_native_method("name2ip", Method::CT_STATIC, _name2ip, 1, 2); }