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