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: