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