ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/slirp/udp.c
Revision: 1.3
Committed: 2005-06-30T21:25:26Z (19 years, 5 months ago) by cebix
Content type: text/plain
Branch: MAIN
CVS Tags: nigel-build-17
Changes since 1.2: +3 -2 lines
Log Message:
minor cleanups

File Contents

# User Rev Content
1 gbeauche 1.1 /*
2     * Copyright (c) 1982, 1986, 1988, 1990, 1993
3     * The Regents of the University of California. All rights reserved.
4     *
5     * Redistribution and use in source and binary forms, with or without
6     * modification, are permitted provided that the following conditions
7     * are met:
8     * 1. Redistributions of source code must retain the above copyright
9     * notice, this list of conditions and the following disclaimer.
10     * 2. Redistributions in binary form must reproduce the above copyright
11     * notice, this list of conditions and the following disclaimer in the
12     * documentation and/or other materials provided with the distribution.
13     * 3. All advertising materials mentioning features or use of this software
14     * must display the following acknowledgement:
15     * This product includes software developed by the University of
16     * California, Berkeley and its contributors.
17     * 4. Neither the name of the University nor the names of its contributors
18     * may be used to endorse or promote products derived from this software
19     * without specific prior written permission.
20     *
21     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24     * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31     * SUCH DAMAGE.
32     *
33     * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94
34     * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp
35     */
36    
37     /*
38     * Changes and additions relating to SLiRP
39     * Copyright (c) 1995 Danny Gasparovski.
40     *
41     * Please read the file COPYRIGHT for the
42     * terms and conditions of the copyright.
43     */
44    
45     #include <slirp.h>
46     #include "ip_icmp.h"
47    
48     struct udpstat udpstat;
49    
50     struct socket udb;
51    
52     /*
53     * UDP protocol implementation.
54     * Per RFC 768, August, 1980.
55     */
56     #ifndef COMPAT_42
57     int udpcksum = 1;
58     #else
59     int udpcksum = 0; /* XXX */
60     #endif
61    
62     struct socket *udp_last_so = &udb;
63    
64     void
65     udp_init()
66     {
67     udb.so_next = udb.so_prev = &udb;
68     }
69     /* m->m_data points at ip packet header
70     * m->m_len length ip packet
71     * ip->ip_len length data (IPDU)
72     */
73     void
74     udp_input(m, iphlen)
75     register struct mbuf *m;
76     int iphlen;
77     {
78     register struct ip *ip;
79     register struct udphdr *uh;
80     /* struct mbuf *opts = 0;*/
81     int len;
82     struct ip save_ip;
83     struct socket *so;
84    
85     DEBUG_CALL("udp_input");
86     DEBUG_ARG("m = %lx", (long)m);
87     DEBUG_ARG("iphlen = %d", iphlen);
88    
89     udpstat.udps_ipackets++;
90    
91     /*
92     * Strip IP options, if any; should skip this,
93     * make available to user, and use on returned packets,
94     * but we don't yet have a way to check the checksum
95     * with options still present.
96     */
97     if(iphlen > sizeof(struct ip)) {
98     ip_stripoptions(m, (struct mbuf *)0);
99     iphlen = sizeof(struct ip);
100     }
101    
102     /*
103     * Get IP and UDP header together in first mbuf.
104     */
105     ip = mtod(m, struct ip *);
106     uh = (struct udphdr *)((caddr_t)ip + iphlen);
107    
108     /*
109     * Make mbuf data length reflect UDP length.
110     * If not enough data to reflect UDP length, drop.
111     */
112     len = ntohs((u_int16_t)uh->uh_ulen);
113    
114     if (ip->ip_len != len) {
115     if (len > ip->ip_len) {
116     udpstat.udps_badlen++;
117     goto bad;
118     }
119     m_adj(m, len - ip->ip_len);
120     ip->ip_len = len;
121     }
122    
123     /*
124     * Save a copy of the IP header in case we want restore it
125     * for sending an ICMP error message in response.
126     */
127     save_ip = *ip;
128     save_ip.ip_len+= iphlen; /* tcp_input subtracts this */
129    
130     /*
131     * Checksum extended UDP header and data.
132     */
133     if (udpcksum && uh->uh_sum) {
134     ((struct ipovly *)ip)->ih_next = 0;
135     ((struct ipovly *)ip)->ih_prev = 0;
136     ((struct ipovly *)ip)->ih_x1 = 0;
137     ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
138     /* keep uh_sum for ICMP reply
139     * uh->uh_sum = cksum(m, len + sizeof (struct ip));
140     * if (uh->uh_sum) {
141     */
142     if(cksum(m, len + sizeof(struct ip))) {
143     udpstat.udps_badsum++;
144     goto bad;
145     }
146     }
147    
148     /*
149     * handle DHCP/BOOTP
150     */
151     if (ntohs(uh->uh_dport) == BOOTP_SERVER) {
152     bootp_input(m);
153     goto bad;
154     }
155    
156     /*
157     * handle TFTP
158     */
159     if (ntohs(uh->uh_dport) == TFTP_SERVER) {
160     tftp_input(m);
161     goto bad;
162     }
163    
164     /*
165     * Locate pcb for datagram.
166     */
167     so = udp_last_so;
168     if (so->so_lport != uh->uh_sport ||
169     so->so_laddr.s_addr != ip->ip_src.s_addr) {
170     struct socket *tmp;
171    
172     for (tmp = udb.so_next; tmp != &udb; tmp = tmp->so_next) {
173     if (tmp->so_lport == uh->uh_sport &&
174     tmp->so_laddr.s_addr == ip->ip_src.s_addr) {
175     tmp->so_faddr.s_addr = ip->ip_dst.s_addr;
176     tmp->so_fport = uh->uh_dport;
177     so = tmp;
178     break;
179     }
180     }
181     if (tmp == &udb) {
182     so = NULL;
183     } else {
184     udpstat.udpps_pcbcachemiss++;
185     udp_last_so = so;
186     }
187     }
188    
189     if (so == NULL) {
190     /*
191     * If there's no socket for this packet,
192     * create one
193     */
194     if ((so = socreate()) == NULL) goto bad;
195     if(udp_attach(so) == -1) {
196     DEBUG_MISC((dfd," udp_attach errno = %d-%s\n",
197     errno,strerror(errno)));
198     sofree(so);
199     goto bad;
200     }
201    
202     /*
203     * Setup fields
204     */
205     /* udp_last_so = so; */
206     so->so_laddr = ip->ip_src;
207     so->so_lport = uh->uh_sport;
208     so->so_faddr = ip->ip_dst; /* XXX */
209     so->so_fport = uh->uh_dport; /* XXX */
210    
211     if ((so->so_iptos = udp_tos(so)) == 0)
212     so->so_iptos = ip->ip_tos;
213    
214     /*
215     * XXXXX Here, check if it's in udpexec_list,
216     * and if it is, do the fork_exec() etc.
217     */
218     }
219    
220     iphlen += sizeof(struct udphdr);
221     m->m_len -= iphlen;
222     m->m_data += iphlen;
223    
224     /*
225     * Now we sendto() the packet.
226     */
227     if (so->so_emu)
228     udp_emu(so, m);
229    
230     if(sosendto(so,m) == -1) {
231     m->m_len += iphlen;
232     m->m_data -= iphlen;
233     *ip=save_ip;
234     DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno)));
235     icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
236     }
237    
238     m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
239    
240     /* restore the orig mbuf packet */
241     m->m_len += iphlen;
242     m->m_data -= iphlen;
243     *ip=save_ip;
244     so->so_m=m; /* ICMP backup */
245    
246     return;
247     bad:
248     m_freem(m);
249     /* if (opts) m_freem(opts); */
250     return;
251     }
252    
253     int udp_output2(struct socket *so, struct mbuf *m,
254     struct sockaddr_in *saddr, struct sockaddr_in *daddr,
255     int iptos)
256     {
257     register struct udpiphdr *ui;
258     int error = 0;
259    
260     DEBUG_CALL("udp_output");
261     DEBUG_ARG("so = %lx", (long)so);
262     DEBUG_ARG("m = %lx", (long)m);
263     DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr);
264     DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr);
265    
266     /*
267     * Adjust for header
268     */
269     m->m_data -= sizeof(struct udpiphdr);
270     m->m_len += sizeof(struct udpiphdr);
271    
272     /*
273     * Fill in mbuf with extended UDP header
274     * and addresses and length put into network format.
275     */
276     ui = mtod(m, struct udpiphdr *);
277     ui->ui_next = ui->ui_prev = 0;
278     ui->ui_x1 = 0;
279     ui->ui_pr = IPPROTO_UDP;
280     ui->ui_len = htons(m->m_len - sizeof(struct ip)); /* + sizeof (struct udphdr)); */
281     /* XXXXX Check for from-one-location sockets, or from-any-location sockets */
282     ui->ui_src = saddr->sin_addr;
283     ui->ui_dst = daddr->sin_addr;
284     ui->ui_sport = saddr->sin_port;
285     ui->ui_dport = daddr->sin_port;
286     ui->ui_ulen = ui->ui_len;
287    
288     /*
289     * Stuff checksum and output datagram.
290     */
291     ui->ui_sum = 0;
292     if (udpcksum) {
293     if ((ui->ui_sum = cksum(m, /* sizeof (struct udpiphdr) + */ m->m_len)) == 0)
294     ui->ui_sum = 0xffff;
295     }
296     ((struct ip *)ui)->ip_len = m->m_len;
297    
298     ((struct ip *)ui)->ip_ttl = ip_defttl;
299     ((struct ip *)ui)->ip_tos = iptos;
300    
301     udpstat.udps_opackets++;
302    
303     error = ip_output(so, m);
304    
305     return (error);
306     }
307    
308     int udp_output(struct socket *so, struct mbuf *m,
309     struct sockaddr_in *addr)
310    
311     {
312     struct sockaddr_in saddr, daddr;
313    
314     saddr = *addr;
315     if ((so->so_faddr.s_addr & htonl(0xffffff00)) == special_addr.s_addr)
316     saddr.sin_addr.s_addr = so->so_faddr.s_addr;
317     daddr.sin_addr = so->so_laddr;
318     daddr.sin_port = so->so_lport;
319    
320     return udp_output2(so, m, &saddr, &daddr, so->so_iptos);
321     }
322    
323     int
324     udp_attach(so)
325     struct socket *so;
326     {
327     struct sockaddr_in addr;
328    
329     if((so->s = socket(AF_INET,SOCK_DGRAM,0)) != -1) {
330     /*
331     * Here, we bind() the socket. Although not really needed
332     * (sendto() on an unbound socket will bind it), it's done
333     * here so that emulation of ytalk etc. don't have to do it
334     */
335     addr.sin_family = AF_INET;
336     addr.sin_port = 0;
337     addr.sin_addr.s_addr = INADDR_ANY;
338     if(bind(so->s, (struct sockaddr *)&addr, sizeof(addr))<0) {
339     int lasterrno=errno;
340     closesocket(so->s);
341     so->s=-1;
342     #ifdef _WIN32
343     WSASetLastError(lasterrno);
344     #else
345     errno=lasterrno;
346     #endif
347     } else {
348     /* success, insert in queue */
349     so->so_expire = curtime + SO_EXPIRE;
350     insque(so,&udb);
351     }
352     }
353     return(so->s);
354     }
355    
356     void
357     udp_detach(so)
358     struct socket *so;
359     {
360     closesocket(so->s);
361     /* if (so->so_m) m_free(so->so_m); done by sofree */
362    
363     sofree(so);
364     }
365    
366     struct tos_t udptos[] = {
367     {0, 53, IPTOS_LOWDELAY, 0}, /* DNS */
368     {517, 517, IPTOS_LOWDELAY, EMU_TALK}, /* talk */
369     {518, 518, IPTOS_LOWDELAY, EMU_NTALK}, /* ntalk */
370     {0, 7648, IPTOS_LOWDELAY, EMU_CUSEEME}, /* Cu-Seeme */
371     {0, 0, 0, 0}
372     };
373    
374     u_int8_t
375     udp_tos(so)
376     struct socket *so;
377     {
378     int i = 0;
379    
380     while(udptos[i].tos) {
381     if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) ||
382     (udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) {
383     so->so_emu = udptos[i].emu;
384     return udptos[i].tos;
385     }
386     i++;
387     }
388    
389     return 0;
390     }
391    
392     #ifdef EMULATE_TALK
393     #include "talkd.h"
394     #endif
395    
396     /*
397     * Here, talk/ytalk/ntalk requests must be emulated
398     */
399     void
400     udp_emu(so, m)
401     struct socket *so;
402     struct mbuf *m;
403     {
404     struct sockaddr_in addr;
405 cebix 1.3 socklen_t addrlen = sizeof(addr);
406 gbeauche 1.1 #ifdef EMULATE_TALK
407     CTL_MSG_OLD *omsg;
408     CTL_MSG *nmsg;
409     char buff[sizeof(CTL_MSG)];
410     u_char type;
411    
412     struct talk_request {
413     struct talk_request *next;
414     struct socket *udp_so;
415     struct socket *tcp_so;
416     } *req;
417    
418     static struct talk_request *req_tbl = 0;
419    
420     #endif
421    
422     struct cu_header {
423 gbeauche 1.2 uint16_t d_family; // destination family
424     uint16_t d_port; // destination port
425     uint32_t d_addr; // destination address
426     uint16_t s_family; // source family
427     uint16_t s_port; // source port
428     uint32_t s_addr; // source address
429     uint32_t seqn; // sequence number
430     uint16_t message; // message
431     uint16_t data_type; // data type
432     uint16_t pkt_len; // packet length
433 gbeauche 1.1 } *cu_head;
434    
435     switch(so->so_emu) {
436    
437     #ifdef EMULATE_TALK
438     case EMU_TALK:
439     case EMU_NTALK:
440     /*
441     * Talk emulation. We always change the ctl_addr to get
442     * some answers from the daemon. When an ANNOUNCE comes,
443     * we send LEAVE_INVITE to the local daemons. Also when a
444     * DELETE comes, we send copies to the local daemons.
445     */
446     if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0)
447     return;
448    
449     #define IS_OLD (so->so_emu == EMU_TALK)
450    
451     #define COPY_MSG(dest, src) { dest->type = src->type; \
452     dest->id_num = src->id_num; \
453     dest->pid = src->pid; \
454     dest->addr = src->addr; \
455     dest->ctl_addr = src->ctl_addr; \
456     memcpy(&dest->l_name, &src->l_name, NAME_SIZE_OLD); \
457     memcpy(&dest->r_name, &src->r_name, NAME_SIZE_OLD); \
458     memcpy(&dest->r_tty, &src->r_tty, TTY_SIZE); }
459    
460     #define OTOSIN(ptr, field) ((struct sockaddr_in *)&ptr->field)
461     /* old_sockaddr to sockaddr_in */
462    
463    
464     if (IS_OLD) { /* old talk */
465     omsg = mtod(m, CTL_MSG_OLD*);
466     nmsg = (CTL_MSG *) buff;
467     type = omsg->type;
468     OTOSIN(omsg, ctl_addr)->sin_port = addr.sin_port;
469     OTOSIN(omsg, ctl_addr)->sin_addr = our_addr;
470     strncpy(omsg->l_name, getlogin(), NAME_SIZE_OLD);
471     } else { /* new talk */
472     omsg = (CTL_MSG_OLD *) buff;
473     nmsg = mtod(m, CTL_MSG *);
474     type = nmsg->type;
475     OTOSIN(nmsg, ctl_addr)->sin_port = addr.sin_port;
476     OTOSIN(nmsg, ctl_addr)->sin_addr = our_addr;
477     strncpy(nmsg->l_name, getlogin(), NAME_SIZE_OLD);
478     }
479    
480     if (type == LOOK_UP)
481     return; /* for LOOK_UP this is enough */
482    
483     if (IS_OLD) { /* make a copy of the message */
484     COPY_MSG(nmsg, omsg);
485     nmsg->vers = 1;
486     nmsg->answer = 0;
487     } else
488     COPY_MSG(omsg, nmsg);
489    
490     /*
491     * If if is an ANNOUNCE message, we go through the
492     * request table to see if a tcp port has already
493     * been redirected for this socket. If not, we solisten()
494     * a new socket and add this entry to the table.
495     * The port number of the tcp socket and our IP
496     * are put to the addr field of the message structures.
497     * Then a LEAVE_INVITE is sent to both local daemon
498     * ports, 517 and 518. This is why we have two copies
499     * of the message, one in old talk and one in new talk
500     * format.
501     */
502    
503     if (type == ANNOUNCE) {
504     int s;
505     u_short temp_port;
506    
507     for(req = req_tbl; req; req = req->next)
508     if (so == req->udp_so)
509     break; /* found it */
510    
511     if (!req) { /* no entry for so, create new */
512     req = (struct talk_request *)
513     malloc(sizeof(struct talk_request));
514     req->udp_so = so;
515     req->tcp_so = solisten(0,
516     OTOSIN(omsg, addr)->sin_addr.s_addr,
517     OTOSIN(omsg, addr)->sin_port,
518     SS_FACCEPTONCE);
519     req->next = req_tbl;
520     req_tbl = req;
521     }
522    
523     /* replace port number in addr field */
524     addrlen = sizeof(addr);
525     getsockname(req->tcp_so->s,
526     (struct sockaddr *) &addr,
527     &addrlen);
528     OTOSIN(omsg, addr)->sin_port = addr.sin_port;
529     OTOSIN(omsg, addr)->sin_addr = our_addr;
530     OTOSIN(nmsg, addr)->sin_port = addr.sin_port;
531     OTOSIN(nmsg, addr)->sin_addr = our_addr;
532    
533     /* send LEAVE_INVITEs */
534     temp_port = OTOSIN(omsg, ctl_addr)->sin_port;
535     OTOSIN(omsg, ctl_addr)->sin_port = 0;
536     OTOSIN(nmsg, ctl_addr)->sin_port = 0;
537     omsg->type = nmsg->type = LEAVE_INVITE;
538    
539     s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
540     addr.sin_addr = our_addr;
541     addr.sin_family = AF_INET;
542     addr.sin_port = htons(517);
543     sendto(s, (char *)omsg, sizeof(*omsg), 0,
544     (struct sockaddr *)&addr, sizeof(addr));
545     addr.sin_port = htons(518);
546     sendto(s, (char *)nmsg, sizeof(*nmsg), 0,
547     (struct sockaddr *) &addr, sizeof(addr));
548     closesocket(s) ;
549    
550     omsg->type = nmsg->type = ANNOUNCE;
551     OTOSIN(omsg, ctl_addr)->sin_port = temp_port;
552     OTOSIN(nmsg, ctl_addr)->sin_port = temp_port;
553     }
554    
555     /*
556     * If it is a DELETE message, we send a copy to the
557     * local daemons. Then we delete the entry corresponding
558     * to our socket from the request table.
559     */
560    
561     if (type == DELETE) {
562     struct talk_request *temp_req, *req_next;
563     int s;
564     u_short temp_port;
565    
566     temp_port = OTOSIN(omsg, ctl_addr)->sin_port;
567     OTOSIN(omsg, ctl_addr)->sin_port = 0;
568     OTOSIN(nmsg, ctl_addr)->sin_port = 0;
569    
570     s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
571     addr.sin_addr = our_addr;
572     addr.sin_family = AF_INET;
573     addr.sin_port = htons(517);
574     sendto(s, (char *)omsg, sizeof(*omsg), 0,
575     (struct sockaddr *)&addr, sizeof(addr));
576     addr.sin_port = htons(518);
577     sendto(s, (char *)nmsg, sizeof(*nmsg), 0,
578     (struct sockaddr *)&addr, sizeof(addr));
579     closesocket(s);
580    
581     OTOSIN(omsg, ctl_addr)->sin_port = temp_port;
582     OTOSIN(nmsg, ctl_addr)->sin_port = temp_port;
583    
584     /* delete table entry */
585     if (so == req_tbl->udp_so) {
586     temp_req = req_tbl;
587     req_tbl = req_tbl->next;
588     free(temp_req);
589     } else {
590     temp_req = req_tbl;
591     for(req = req_tbl->next; req; req = req_next) {
592     req_next = req->next;
593     if (so == req->udp_so) {
594     temp_req->next = req_next;
595     free(req);
596     break;
597     } else {
598     temp_req = req;
599     }
600     }
601     }
602     }
603    
604     return;
605     #endif
606    
607     case EMU_CUSEEME:
608    
609     /*
610     * Cu-SeeMe emulation.
611     * Hopefully the packet is more that 16 bytes long. We don't
612     * do any other tests, just replace the address and port
613     * fields.
614     */
615     if (m->m_len >= sizeof (*cu_head)) {
616     if (getsockname(so->s, (struct sockaddr *)&addr, &addrlen) < 0)
617     return;
618     cu_head = mtod(m, struct cu_header *);
619 gbeauche 1.2 cu_head->s_port = addr.sin_port;
620     cu_head->s_addr = our_addr.s_addr;
621 gbeauche 1.1 }
622    
623     return;
624     }
625     }
626    
627     struct socket *
628     udp_listen(port, laddr, lport, flags)
629     u_int port;
630     u_int32_t laddr;
631     u_int lport;
632     int flags;
633     {
634     struct sockaddr_in addr;
635     struct socket *so;
636 cebix 1.3 socklen_t addrlen = sizeof(struct sockaddr_in);
637     int opt = 1;
638 gbeauche 1.1
639     if ((so = socreate()) == NULL) {
640     free(so);
641     return NULL;
642     }
643     so->s = socket(AF_INET,SOCK_DGRAM,0);
644     so->so_expire = curtime + SO_EXPIRE;
645     insque(so,&udb);
646    
647     addr.sin_family = AF_INET;
648     addr.sin_addr.s_addr = INADDR_ANY;
649     addr.sin_port = port;
650    
651     if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0) {
652     udp_detach(so);
653     return NULL;
654     }
655     setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
656     /* setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int)); */
657    
658     getsockname(so->s,(struct sockaddr *)&addr,&addrlen);
659     so->so_fport = addr.sin_port;
660     if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
661     so->so_faddr = our_addr;
662     else
663     so->so_faddr = addr.sin_addr;
664    
665     so->so_lport = lport;
666     so->so_laddr.s_addr = laddr;
667     if (flags != SS_FACCEPTONCE)
668     so->so_expire = 0;
669    
670     so->so_state = SS_ISFCONNECTED;
671    
672     return so;
673     }