|
|
| version 1.2, 2008/06/16 13:09:22 | version 1.22, 2026/07/19 14:25:42 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: @b inet parser class. | Parser: @b inet parser class. |
| Copyright(c) 2001-2005 ArtLebedev Group(http://www.artlebedev.com) | Copyright (c) 2001-2026 Art. Lebedev Studio (https://www.artlebedev.com) |
| Author: Alexandr Petrosian <paf@design.ru>(http://paf.design.ru) | Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru> |
| */ | */ |
| static const char * const IDENT_INET_C="$Date$"; | |
| #include "pa_vmethod_frame.h" | #include "pa_vmethod_frame.h" |
| #include "pa_request.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$"; | |
| class MInet: public Methoded { | class MInet: public Methoded { |
| public: | public: |
| MInet(); | MInet(); |
| public: // Methoded | |
| bool used_directly() { return true; } | |
| }; | }; |
| // global variables | // global variables |
| DECLARE_CLASS_VAR(inet, new MInet, 0); | DECLARE_CLASS_VAR(inet, new MInet); |
| static void _ntoa(Request& r, MethodParams& params){ | static void _ntoa(Request& r, MethodParams& params){ |
| unsigned long l=(unsigned long)trunc(params.as_double(0, "parameter must be expression", r)); | 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]; | 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", | r.write(*new String(ip_cstr)); |
| (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){ | static void _aton(Request& r, MethodParams& params){ |
| const String ip=params.as_string(0, PARAMETER_MUST_BE_STRING); | const String ip=params.as_string(0, PARAMETER_MUST_BE_STRING); |
| if(ip.is_empty()) | if(ip.is_empty()) |
| throw Exception(PARSER_RUNTIME, | throw Exception(PARSER_RUNTIME, 0, "IP address must not be empty."); |
| 0, | |
| "IP address must not be empty."); | |
| const char* ip_cstr=ip.cstr(); | const char* ip_cstr=ip.cstr(); |
| ulong result=0; | ulong result=0; |
| Line 52 static void _aton(Request& r, MethodPara | Line 47 static void _aton(Request& r, MethodPara |
| bool err=false; | bool err=false; |
| const char* p=ip_cstr; | const char* p=ip_cstr; |
| while(char c=*p++){ | while(char c=*p++){ |
| uint digit=(uint)(c-'0'); // assume ascii | int digit=(int)(c-'0'); // assume ascii |
| if(digit>=0 && digit<=9){ | if(digit>=0 && digit<=9){ |
| byte_start=false; | byte_start=false; |
| if((byte_value=byte_value*10+digit) > 255){ | if((byte_value=byte_value*10+(uint)digit) > 255){ |
| err=true; | err=true; |
| break; | break; |
| } | } |
| Line 76 static void _aton(Request& r, MethodPara | Line 71 static void _aton(Request& r, MethodPara |
| } | } |
| if(err || dot_cnt!=3 || byte_start){ | if(err || dot_cnt!=3 || byte_start){ |
| throw Exception(PARSER_RUNTIME, | throw Exception(PARSER_RUNTIME, 0, "Invalid IP address '%s' specified.", ip_cstr); |
| 0, | |
| "Invalid IP address '%s' specified.", ip_cstr); | |
| } else { | } else { |
| result=(result << 8)+(ulong)byte_value; | result=(result << 8)+(ulong)byte_value; |
| r.write_no_lang(*new VDouble(result)); | r.write(*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(*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(*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)); | |
| } | |
| } | |
| // also used in curl.C, returns first address or fills the table if not null | |
| const char *pa_name2ip(const char *name, int family, Table *table, const char *exception_type){ | |
| struct addrinfo hints, *info; | |
| memset(&hints, 0, sizeof(hints)); | |
| hints.ai_family=family; | |
| hints.ai_socktype=SOCK_STREAM; | |
| int error=getaddrinfo(name, NULL, &hints, &info); | |
| if(error) | |
| throw Exception(exception_type, 0, "Can't resolve domain name '%s': %s", name, gai_strerror(error)); | |
| const char *result=0; | |
| 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(exception_type, 0, "Can't translate address: %s", gai_strerror(error)); | |
| if(table){ | |
| Table::element_type row(new ArrayString()); | |
| static const String sv4("4"), sv6("6"), sunknown("unknown"); | |
| *row+=new String(pa_strdup(hbuf), String::L_TAINTED); | |
| *row+=cur->ai_family == AF_INET ? &sv4 : cur->ai_family == AF_INET6 ? &sv6 : &sunknown; | |
| *table+=row; | |
| } else { | |
| result=pa_strdup(hbuf); | |
| break; | |
| } | |
| } | } |
| freeaddrinfo(info); | |
| return result; | |
| } | |
| 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()); | |
| int family=AF_INET; | |
| Table *table=NULL; | |
| if(params.count() == 2) | |
| if(HashStringValue* options=params.as_hash(1)){ | |
| int valid_options=0; | |
| if(Value* value=options->get("ipv")){ | |
| family=ipv_format(r.process(*value).as_string()); | |
| valid_options++; | |
| } | |
| if(Value* value=options->get("table")){ | |
| if(r.process(*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); | |
| } | |
| if(const char *ip=pa_name2ip(name_cstr, family, table, PARSER_RUNTIME)) | |
| r.write(*new String(ip, String::L_TAINTED)); | |
| if(table) | |
| r.write(*new VTable(table)); | |
| } | |
| static void _hostname(Request& r, MethodParams&){ | |
| char buf[MAX_STRING]; | |
| if(gethostname(buf, MAX_STRING)) | |
| throw Exception(PARSER_RUNTIME, 0, "Cant't get hostname"); | |
| r.write(*new String(pa_strdup(buf), String::L_TAINTED)); | |
| } | } |
| // constructor | // constructor |
| MInet::MInet(): Methoded("inet") { | MInet::MInet(): Methoded("inet") { |
| add_native_method("ntoa", Method::CT_STATIC, _ntoa, 1, 1); | add_native_method("ntoa", Method::CT_STATIC, _ntoa, 1, 1); |
| add_native_method("aton", Method::CT_STATIC, _aton, 1, 1); | add_native_method("aton", Method::CT_STATIC, _aton, 1, 1); |
| // ^inet:ip2name[ip; $.ipv[4|6|any] ] | |
| // todo: gethostbyname, gethostbyaddr | 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); | |
| // ^inet:hostname[] | |
| add_native_method("hostname", Method::CT_STATIC, _hostname, 0, 0); | |
| } | } |