Annotation of parser3/src/classes/inet.C, revision 1.7
1.1 misha 1: /** @file
2: Parser: @b inet parser class.
3:
1.5 moko 4: Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com)
1.1 misha 5: Author: Alexandr Petrosian <paf@design.ru>(http://paf.design.ru)
6: */
7:
8: #include "pa_vmethod_frame.h"
9: #include "pa_request.h"
1.7 ! moko 10: #include "pa_vtable.h"
1.1 misha 11:
1.7 ! moko 12: volatile const char * IDENT_INET_C="$Id: inet.C,v 1.6 2012/04/19 19:41:29 moko Exp $";
1.5 moko 13:
1.1 misha 14: class MInet: public Methoded {
15: public:
16: MInet();
17: };
18:
19: // global variables
20:
21: DECLARE_CLASS_VAR(inet, new MInet, 0);
22:
23:
24: static void _ntoa(Request& r, MethodParams& params){
25: unsigned long l=(unsigned long)trunc(params.as_double(0, "parameter must be expression", r));
1.2 misha 26: static const int ip_cstr_bufsize=3*4+3+1+1;
1.1 misha 27: char* ip_cstr=new(PointerFreeGC) char[ip_cstr_bufsize];
1.7 ! moko 28: snprintf(ip_cstr, ip_cstr_bufsize, "%u.%u.%u.%u", (l>>24) & 0xFF, (l>>16) & 0xFF, (l>>8) & 0xFF, l & 0xFF);
1.1 misha 29: r.write_no_lang(*new String(ip_cstr));
30: }
31:
32: static void _aton(Request& r, MethodParams& params){
33: const String ip=params.as_string(0, PARAMETER_MUST_BE_STRING);
34: if(ip.is_empty())
1.7 ! moko 35: throw Exception(PARSER_RUNTIME, 0, "IP address must not be empty.");
1.1 misha 36:
37: const char* ip_cstr=ip.cstr();
38: ulong result=0;
39: uint byte_value=0;
40: uint dot_cnt=0;
41: bool byte_start=true;
42: bool err=false;
43: const char* p=ip_cstr;
44: while(char c=*p++){
1.6 moko 45: int digit=(int)(c-'0'); // assume ascii
1.1 misha 46: if(digit>=0 && digit<=9){
47: byte_start=false;
1.6 moko 48: if((byte_value=byte_value*10+(uint)digit) > 255){
1.1 misha 49: err=true;
50: break;
51: }
52: } else if(c=='.'){
53: if(byte_start){ // two dots in row or IP started with dot
54: err=true;
55: break;
56: } else {
57: byte_start=true;
58: dot_cnt++;
59: result=(result << 8)+(ulong)byte_value;
60: byte_value=0;
61: }
62: } else { // invalid char
63: err=true;
64: break;
65: }
66: }
67:
68: if(err || dot_cnt!=3 || byte_start){
1.7 ! moko 69: throw Exception(PARSER_RUNTIME, 0, "Invalid IP address '%s' specified.", ip_cstr);
1.1 misha 70: } else {
71: result=(result << 8)+(ulong)byte_value;
72: r.write_no_lang(*new VDouble(result));
73: }
74: }
75:
1.7 ! moko 76: int ipv_format(const String &value){
! 77: if(value == "4") return AF_INET;
! 78: if(value == "6") return AF_INET6;
! 79: if(value == "any") return AF_UNSPEC;
! 80: throw Exception(PARSER_RUNTIME, &value, "ipv option value must be 4 or 6 or any");
! 81: }
! 82:
! 83: static void _ip2name(Request& r, MethodParams& params){
! 84: const String sip=params.as_string(0, PARAMETER_MUST_BE_STRING);
! 85: if(sip.is_empty())
! 86: throw Exception(PARSER_RUNTIME, 0, "IP address must not be empty.");
! 87:
! 88: const char* ip_cstr=sip.cstr();
! 89:
! 90: struct addrinfo hints, *info=0;
! 91: memset(&hints, 0, sizeof(hints));
! 92: hints.ai_family=AF_INET;
! 93: hints.ai_socktype=SOCK_STREAM;
! 94: hints.ai_flags=AI_NUMERICHOST; // to disable DNS lookup
! 95:
! 96: if(params.count() == 2)
! 97: if(HashStringValue* options=params.as_hash(1)){
! 98: int valid_options=0;
! 99: if(Value* value=options->get("ipv")){
! 100: hints.ai_family=ipv_format(r.process_to_value(*value).as_string());
! 101: valid_options++;
! 102: }
! 103: if(valid_options!=options->count())
! 104: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
! 105: }
! 106:
! 107: int error=getaddrinfo(ip_cstr, 0, &hints, &info);
! 108: if(error==EAI_NONAME)
! 109: throw Exception(PARSER_RUNTIME, 0, "Invalid IP address '%s' specified", ip_cstr);
! 110: if(error)
! 111: throw Exception(PARSER_RUNTIME, 0, "Invalid IP address '%s': %s", ip_cstr, gai_strerror(error));
! 112:
! 113: char hbuf[NI_MAXHOST];
! 114: error=getnameinfo(info->ai_addr, info->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD);
! 115:
! 116: freeaddrinfo(info);
! 117:
! 118: if(!error){
! 119: r.write_no_lang(*new String(pa_idna_decode(pa_strdup(hbuf), r.charsets.source()), String::L_TAINTED));
! 120: } else if(error!=EAI_NONAME){
! 121: throw Exception(PARSER_RUNTIME, 0, "Can't resolve IP address '%s': %s", ip_cstr, gai_strerror(error));
! 122: }
! 123: }
! 124: static void _name2ip(Request& r, MethodParams& params){
! 125: const String sname=params.as_string(0, PARAMETER_MUST_BE_STRING);
! 126: if(sname.is_empty())
! 127: throw Exception(PARSER_RUNTIME, 0, "Domain name must not be empty.");
! 128:
! 129: const char* name_cstr=pa_idna_encode(sname.cstr(), r.charsets.source());
! 130:
! 131: struct addrinfo hints, *info;
! 132: memset(&hints, 0, sizeof(hints));
! 133: hints.ai_family=AF_INET;
! 134: hints.ai_socktype=SOCK_STREAM;
! 135:
! 136: Table *table=NULL;
! 137:
! 138: if(params.count() == 2)
! 139: if(HashStringValue* options=params.as_hash(1)){
! 140: int valid_options=0;
! 141: if(Value* value=options->get("ipv")){
! 142: hints.ai_family=ipv_format(r.process_to_value(*value).as_string());
! 143: valid_options++;
! 144: }
! 145: if(Value* value=options->get("table")){
! 146: if(r.process_to_value(*value).as_bool()){
! 147: Table::columns_type columns(new ArrayString);
! 148: static const String sip("ip"), sversion("version");
! 149: *columns+=&sip; *columns+=&sversion;
! 150: table=new Table(columns);
! 151: }
! 152: valid_options++;
! 153: }
! 154: if(valid_options!=options->count())
! 155: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
! 156: }
! 157:
! 158: int error=getaddrinfo(name_cstr, NULL, &hints, &info);
! 159:
! 160: if(error)
! 161: throw Exception(PARSER_RUNTIME, 0, "Can't resolve domain name '%s': %s", name_cstr, gai_strerror(error));
! 162:
! 163: char hbuf[INET6_ADDRSTRLEN];
! 164:
! 165: for(struct addrinfo *cur=info; cur; cur=cur->ai_next) {
! 166: if(error=getnameinfo(cur->ai_addr, cur->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST))
! 167: throw Exception(PARSER_RUNTIME, 0, "Can't translate address: %s", gai_strerror(error));
! 168: String *saddr=new String(pa_strdup(hbuf), String::L_TAINTED);
! 169: if(table){
! 170: Table::element_type row(new ArrayString());
! 171: static const String sv4("4"), sv6("6"), sunknown("unknown");
! 172: *row+=saddr;
! 173: *row+=cur->ai_family == AF_INET ? &sv4 : cur->ai_family == AF_INET6 ? &sv6 : &sunknown;
! 174: *table+=row;
! 175: } else {
! 176: r.write_no_lang(*saddr);
! 177: break;
! 178: }
! 179: }
! 180:
! 181: if(table)
! 182: r.write_no_lang(*new VTable(table));
! 183:
! 184: freeaddrinfo(info);
! 185: }
! 186:
1.1 misha 187: // constructor
188: MInet::MInet(): Methoded("inet") {
189: add_native_method("ntoa", Method::CT_STATIC, _ntoa, 1, 1);
190: add_native_method("aton", Method::CT_STATIC, _aton, 1, 1);
1.7 ! moko 191: // ^inet:ip2name[ip; $.ipv[4|6|any] ]
! 192: add_native_method("ip2name", Method::CT_STATIC, _ip2name, 1, 2);
! 193: // ^inet:name2ip[name; $.ipv[4|6|any] $.table(true) ]
! 194: add_native_method("name2ip", Method::CT_STATIC, _name2ip, 1, 2);
1.1 misha 195: }
E-mail: