chiark / gitweb /
dgram compiles and works; dispatch()
[chiark-tcl.git] / dgram / sockaddr.c
1 /*
2  * struct sockaddr
3  *
4  * syntaxes:
5  *    ddd.ddd.ddd.ddd,nnnn       IPv4 address and port
6  *                                 ... host or port may be `*'
7  *    /abs/path/to/socket        AF_UNIX
8  *    ./rel/path/to/socket       AF_UNIX
9  */
10
11 #include "dgram.h"
12
13 #define SOCKADDR_LEN(sa) ((sa)->end - (sa)->begin)
14
15 /* parsing */
16
17 int cht_pat_sockaddr(Tcl_Interp *ip, Tcl_Obj *o, SockAddr_Value *val) {
18   int rc;
19
20   rc= Tcl_ConvertToType(ip,o,&sockaddr_type);
21   if (rc) return rc;
22
23   *val= *OBJ_SOCKADDR(o);
24   return TCL_OK;
25 }
26   
27 Tcl_Obj *cht_ret_sockaddr(Tcl_Interp *ip, SockAddr_Value val) {
28   Tcl_Obj *o;
29
30   o= Tcl_NewObj();
31   Tcl_InvalidateStringRep(o);
32   *OBJ_SOCKADDR(o)= val;
33   o->typePtr= &sockaddr_type;
34   return o;
35 }
36
37 /* native type methods */
38
39 void cht_sockaddr_clear(SockAddr_Value *v) { v->begin= v->end= 0; }
40
41 void cht_sockaddr_create(SockAddr_Value *v, const struct sockaddr *a, int al) {
42   Byte *begin;
43   
44   v->begin= begin= TALLOC(al);
45   memcpy(begin, a, al);
46   v->end= begin + al;
47 }  
48
49 int cht_sockaddr_len(const SockAddr_Value *v) {
50   return SOCKADDR_LEN(v);
51 }
52
53 const struct sockaddr *cht_sockaddr_addr(const SockAddr_Value *v) {
54   return (const void*)v->begin;
55 }
56   
57 void cht_sockaddr_free(const SockAddr_Value *v) {
58   TFREE(v->begin);
59 }
60
61 /* Sockaddr Tcl type */
62
63 static void sockaddr_t_dup(Tcl_Obj *src, Tcl_Obj *dup) {
64   cht_sockaddr_create(OBJ_SOCKADDR(dup),
65                   cht_sockaddr_addr(OBJ_SOCKADDR(src)),
66                   cht_sockaddr_len(OBJ_SOCKADDR(src)));
67   dup->typePtr= &cht_hbytes_type;
68 }
69
70 static void sockaddr_t_free(Tcl_Obj *o) {
71   cht_sockaddr_free(OBJ_SOCKADDR(o));
72 }
73
74 static void sockaddr_t_ustr(Tcl_Obj *o) {
75   const struct sockaddr *sa;
76   char i46buf[INET6_ADDRSTRLEN], portbuf[50];
77   const struct sockaddr_in *sin;
78   int al;
79   const char *string, *prepend;
80   
81   sa= cht_sockaddr_addr(OBJ_SOCKADDR(o));
82   al= cht_sockaddr_len(OBJ_SOCKADDR(o));
83     
84   switch (sa->sa_family) {
85   case AF_INET:
86   case AF_INET6:
87     assert(sizeof(i46buf) >= INET_ADDRSTRLEN);
88     assert(al >= sizeof(struct sockaddr_in));
89     sin= (const void*)sa;
90     inet_ntop(sa->sa_family, &sin->sin_addr, i46buf, al);
91     snprintf(portbuf,sizeof(portbuf),",%d",(int)ntohs(sin->sin_port));
92     prepend= i46buf;
93     string= portbuf;
94     break;
95
96   case AF_UNIX:
97     string= ((const struct sockaddr_un*)sa)->sun_path;
98     prepend= "";
99     if (!string[0]) string="//"; 
100     else if (string[0] != '/' || string[1] == '/') prepend= "./";
101     break;
102
103   default: /* ouch ! */
104     cht_obj_updatestr_array_prefix(o,(const void*)sa,al,"?");
105     return;
106   }
107
108   cht_obj_updatestr_vstringls(o,
109                           prepend, strlen(prepend),
110                           string, strlen(string),
111                           (char*)0);
112 }
113
114 static int sockaddr_t_sfa(Tcl_Interp *ip, Tcl_Obj *o) {
115   union {
116     struct sockaddr_un sun;
117     struct sockaddr_in sin;
118   } s;
119   unsigned long port_l;
120   
121   char *str, *ep, *copy;
122   int sl, pl, iprv;
123   const char *comma, *path;
124
125   str= Tcl_GetStringFromObj(o,0);  assert(str);
126   cht_objfreeir(o);
127   memset(&s,0,sizeof(s));
128
129   if (str[0]=='/' || (str[0]=='.' && str[1]=='/')) {
130     
131     sl= sizeof(s.sun);
132     s.sun.sun_family= AF_UNIX;
133
134     if (!strcmp(str,"//")) path= "";
135     else if (!memcmp(str,"./",2) && str[2]) path= str+2;
136     else { assert(str[0]=='/' && str[1]!='/'); path=str; }
137
138     if (strlen(str) >= sizeof(s.sun.sun_path))
139       return cht_staticerr(ip, "AF_UNIX path too long", "SOCKADDR AFUNIX LENGTH");
140
141     strcpy(s.sun.sun_path, path);
142
143   } else if ((comma= strchr(str, ','))) {
144
145     sl= sizeof(s.sin);
146     s.sin.sin_family= AF_INET;
147
148     pl= comma - str;
149     copy= TALLOC(pl+1);
150     memcpy(copy, str, pl);
151     copy[pl]= 0;
152
153     if (!strcmp(copy,"*")) {
154       s.sin.sin_addr.s_addr= INADDR_ANY;
155       iprv= 1;
156     } else {
157       iprv= inet_pton(AF_INET, copy, &s.sin.sin_addr);
158     }
159     TFREE(copy);
160
161     if (!iprv)
162       return cht_staticerr(ip, "bad IPv4 address syntax", "SOCKADDR SYNTAX IPV4");
163
164     comma++;
165     if (!strcmp(comma,"*")) {
166       s.sin.sin_port= 0;
167     } else {
168       errno=0; port_l=strtoul(comma,&ep,10);
169       if (errno || *ep)
170         return cht_staticerr(ip, "bad IPv4 port", "SOCKADDR SYNTAX IPV4");
171       if (port_l > 65535)
172         return cht_staticerr(ip, "IPv4 port out of range", "SOCKADDR SYNTAX IPV4");
173       s.sin.sin_port= htons(port_l);
174     }
175
176   } else {
177
178     return cht_staticerr(ip, "bad socket address syntax", "SOCKADDR SYNTAX OTHER");
179
180   }
181
182   cht_sockaddr_create(OBJ_SOCKADDR(o), (void*)&s, sl);
183   
184   o->typePtr = &sockaddr_type;
185   return TCL_OK;
186 }
187
188 Tcl_ObjType sockaddr_type = {
189   "sockaddr",
190   sockaddr_t_free, sockaddr_t_dup, sockaddr_t_ustr, sockaddr_t_sfa
191 };