home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Entertainment / MacMud / Sockets / socket.udp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-03-25  |  10.5 KB  |  449 lines  |  [TEXT/MPS ]

  1. /*
  2.  *    A more or less BSD compatable socket library for MacTCP
  3.  *    
  4.  *    Summer 1989, Tom Milligan, University of Toronto Computing Services
  5.  */
  6.  
  7.      
  8. #include <Events.h>
  9. #include <memory.h>
  10. #include <errors.h>
  11. #include <types.h>
  12. #include <OSUtils.h> /* for SysBeep */
  13.  
  14. #include <stdio.h>
  15.  
  16. #include <sys/types.h>
  17. #include <sys/time.h>
  18. #include <sys/errno.h>
  19. #include <sys/socket.h>
  20. #include <netinet/in.h>
  21. #include <sys/uio.h>
  22.  
  23. #include "tcpglue.h"
  24. #include "socket.internal.h"
  25.  
  26.  
  27. /*
  28.  * asynchronous notification routine 
  29.  */
  30. static int notified = 0;
  31. static int lastNotifyCount = 0;
  32.  
  33. static StreamPtr notifyUdpStream;
  34. static unsigned short notifyEventCode;
  35. static Ptr notifyUserDataPtr;
  36. static unsigned short notifyTerminReason;
  37. static struct ICMPReport *notifyIcmpMsg;
  38.  
  39. pascal void sock_udp_notify(udpStream,eventCode,userDataPtr,icmpMsg)
  40.     StreamPtr udpStream;
  41.     unsigned short eventCode; 
  42.     Ptr userDataPtr;
  43.     struct ICMPReport *icmpMsg;
  44. {
  45.     notified++;
  46.  
  47.     notifyUdpStream = udpStream;
  48.     notifyEventCode = eventCode;
  49.     notifyUserDataPtr = userDataPtr;
  50.     notifyIcmpMsg = icmpMsg;
  51. }
  52.  
  53. static char *eventNames[] = 
  54. {
  55.     "event 0",
  56.     "data arrival",
  57.     "ICMP message"
  58. };
  59. static char *icmpMessages[] =
  60. {
  61.     "net unreachable",
  62.     "host unreachable",
  63.     "protocol unreachable",
  64.     "port unreachable",
  65.     "fragmentation required",
  66.     "source route failed",
  67.     "time exceeded",
  68.     "parameter problem",
  69.     "missing required option"
  70. };
  71. udpCheckNotify()
  72. {
  73.     if (notified == lastNotifyCount)
  74.         return(0);
  75.     
  76.     lastNotifyCount = notified;
  77.     dprintf("notify count is now %d\n",lastNotifyCount);
  78.     dprintf("stream %08x\n",notifyUdpStream);
  79.     dprintf("event %d '%s'\n",notifyEventCode,eventNames[notifyEventCode]);
  80.     if (notifyEventCode == UDPDataArrival)
  81.         dprintf("%d bytes\n",notifyTerminReason/*!?*/);
  82.     dprintf("icmp msg %08x\n",notifyIcmpMsg);
  83.     if (notifyEventCode == UDPICMPReceived)
  84.     {
  85.         dprintf("stream %08x\n",notifyIcmpMsg->streamPtr);
  86.         dprintf("local %08x/%d\n",notifyIcmpMsg->localHost,notifyIcmpMsg->localPort);
  87.         dprintf("remote %08x/%d\n",notifyIcmpMsg->remoteHost,notifyIcmpMsg->remotePort);
  88.         dprintf("%s\n",icmpMessages[notifyIcmpMsg->reportType]);
  89.         dprintf("optionalAddlInfo %04x\n",notifyIcmpMsg->optionalAddlInfo);
  90.         dprintf("optionalAddlInfoPtr %08x\n",notifyIcmpMsg->optionalAddlInfoPtr);
  91.     }
  92.     dprintf("userdata %s\n",notifyUserDataPtr);
  93.     return(1);
  94. }
  95.  
  96. /*
  97.  * sock_udp_new_stream()  - create the MacTCP stream for this socket. not
  98.  *                          called till the last minute while we wait for
  99.  *                          a bind to come in.
  100.  *
  101.  *                          called from whichever of connect, recv, send or 
  102.  *                          select (can_recv or can_send) is called first.
  103.  */
  104. sock_udp_new_stream(sp)
  105.     SocketPtr sp;
  106. {
  107.     OSErr io;
  108.     
  109. #if SOCK_UDP_DEBUG >= 2
  110.     dprintf("sock_udp_new_stream: sp %08x port %d\n", sp, sp->sa.sin_port);
  111. #endif
  112.     
  113.     io = xUDPCreate(STREAM_BUFFER_SIZE,
  114.                     sock_udp_notify, 
  115.                     sp->sa.sin_port, 
  116.                     &sp->pb);
  117.     switch (io)
  118.     {
  119.         case noErr:                 break;
  120.         case streamAlreadyOpen:     return(sock_err(io));
  121.         case invalidLength:         return(sock_err(ENOBUFS));
  122.         case invalidBufPtr:         return(sock_err(ENOBUFS));
  123.         case duplicateSocket:        return(sock_err(EADDRINUSE));
  124.         case insufficientResources: return(sock_err(EMFILE));
  125.         default: /* error from PBOpen */ return(sock_err(ENETDOWN));
  126.     }
  127.     sp->pb.udp.ioResult = noErr;
  128.  
  129.     sp->apb.pb.udp.udpStream = sp->pb.udp.udpStream;
  130.     sp->apb.pb.udp.ioResult = noErr;
  131.     sp->apb.sp = sp;
  132.     
  133.     sp->wpb[0].pb.udp.udpStream = sp->pb.udp.udpStream;
  134.     sp->wpb[0].pb.udp.ioResult = noErr;
  135.     sp->wpb[0].sp = sp;
  136.     sp->nextwpb = 0;
  137.  
  138.     sp->sa.sin_port = sp->pb.udp.csParam.create.localPort;
  139.     sp->sstate = SOCK_STATE_UNCONNECTED;
  140.     
  141.     /* start up the read ahead */
  142.     return(sock_udp_read_ahead(sp));
  143. }
  144.  
  145.  
  146. /*
  147.  *    sock_udp_connect() - sets the peer process we will be talking to
  148.  */
  149. sock_udp_connect(sp,addr)
  150.     SocketPtr sp;
  151.     struct sockaddr_in *addr;
  152. {
  153.     int status;
  154.     
  155.     /* make the stream if its not made already */
  156.     if (sp->sstate == SOCK_STATE_NO_STREAM)
  157.     {
  158.         status = sock_udp_new_stream(sp);
  159.         if (status != 0)
  160.             return(status);
  161.     }
  162.     
  163.     /* record our peer */
  164.     sp->peer.sin_addr.s_addr = addr->sin_addr.s_addr;
  165.     sp->peer.sin_port = addr->sin_port;
  166.     sp->sstate = SOCK_STATE_CONNECTED;
  167.     
  168.     /* flush the read-ahead buffer if its not from our new friend */
  169.     (void) sock_udp_can_recv(sp);
  170.     
  171.     return(0);
  172. }
  173.  
  174.  
  175. /*
  176.  * sock_udp_read_ahead() - start up the one packet read-ahead
  177.  *
  178.  *                         called from new_stream, recv and can_recv
  179.  */
  180. static 
  181. sock_udp_read_ahead(sp)
  182.     SocketPtr sp;
  183. {
  184.     OSErr io;
  185.     
  186.     io = xUDPRead(&sp->apb.pb, -1L);
  187.     if (io != noErr) 
  188.         return(sock_err(io));
  189.  
  190.     return(0);
  191. }
  192.  
  193. /*
  194.  * sock_udp_return_buffer() - return the receive buffer to MacTCP
  195.  */
  196. static 
  197. sock_udp_return_buffer(sp)
  198.     SocketPtr sp;
  199. {    
  200.     OSErr io;
  201.     
  202.     if (sp->apb.pb.udp.csParam.receive.rcvBuffLen > 0)
  203.     {
  204.         io = xUDPBfrReturn(&sp->apb.pb);
  205.         if (io != noErr)
  206.             return(sock_err(io));
  207.     }
  208.     return(0);
  209. }
  210.  
  211. /*
  212.  * sock_udp_recv()
  213.  *
  214.  * returns bytes received or -1 and errno
  215.  */
  216. sock_udp_recv(sp, buffer, buflen, flags/* ignored */, from, fromlen)
  217.     SocketPtr sp;
  218.     char *buffer;
  219.     int buflen;    
  220.     int flags;
  221.     struct sockaddr_in *from;
  222.     int *fromlen;
  223. {
  224. #pragma unused(flags)
  225.     struct sockaddr_in peer;
  226.     
  227. #if SOCK_UDP_DEBUG >= 2
  228.     dprintf("sock_udp_recv: sp %08x buflen %d state %04x\n", sp,buflen,sp->sstate);
  229. #endif
  230.  
  231.     /* make the stream if its not made already */
  232.     if (sp->sstate == SOCK_STATE_NO_STREAM)
  233.     {
  234.         int status = sock_udp_new_stream(sp);
  235.         if (status != 0)
  236.             return(status);
  237.     }
  238.     
  239.     /* dont block a non-blocking socket */
  240.     if (sp->nonblocking && !sock_udp_can_recv(sp))
  241.         return(sock_err(EWOULDBLOCK));
  242.         
  243. #if SOCK_UDP_DEBUG >= 5
  244.     dprintf("about to spin on recv\n");
  245. #endif
  246.     while (!sock_udp_can_recv(sp))
  247.     {
  248. #if SOCK_UDP_DEBUG >= 5
  249.         udpCheckNotify();
  250. #endif
  251.         if (spinroutine != NULL)
  252.             (*spinroutine)();
  253.     }
  254. #if SOCK_UDP_DEBUG >= 5
  255.     dprintf("spin done: ioResult %d, got %d bytes from %08x/%d\n",
  256.             sp->apb.pb.udp.ioResult,
  257.             sp->apb.pb.udp.csParam.receive.rcvBuffLen,
  258.             sp->apb.pb.udp.csParam.receive.remoteHost,
  259.             sp->apb.pb.udp.csParam.receive.remotePort);
  260. #endif
  261. #if SOCK_UDP_DEBUG >= 3
  262.     if (sp->apb.pb.udp.ioResult != noErr)
  263.         dprintf("udp_read_error %d\n",sp->apb.pb.udp.ioResult);
  264. #endif
  265.  
  266.     switch(sp->apb.pb.udp.ioResult)
  267.     {
  268.         case noErr:
  269.             break;
  270.         case connectionTerminated:
  271.         case commandTimeout:
  272.         case invalidStreamPtr:
  273.         default:
  274.             return(sock_err(sp->apb.pb.udp.ioResult));
  275.     }
  276.  
  277.     /* return the data to the user - truncate the packet if necessary */
  278.     buflen = min(buflen,sp->apb.pb.udp.csParam.receive.rcvBuffLen);    
  279.     BlockMove(sp->apb.pb.udp.csParam.receive.rcvBuff,buffer,buflen);
  280. #if (SOCK_UDP_DEBUG >= 7) || defined(UDP_PACKET_TRACE)
  281.  
  282. #ifdef 0
  283.     hex_dump(buffer, buflen, "udp from %08x/%d\n",
  284.             sp->apb.pb.udp.csParam.receive.remoteHost,
  285.             sp->apb.pb.udp.csParam.receive.remotePort);
  286. #endif
  287.  
  288. #endif
  289.     if (from != NULL)
  290.     {
  291.         peer.sin_family      = AF_INET;
  292.         peer.sin_addr.s_addr = sp->apb.pb.udp.csParam.receive.remoteHost;
  293.         peer.sin_port        = sp->apb.pb.udp.csParam.receive.remotePort;
  294.         bzero(&peer.sin_zero[0], 8);
  295.         sock_copy_addr(&peer,from,fromlen);
  296.     }    
  297.     
  298.     /* continue the read-ahead - errors which occur */
  299.     /* here will show up next time around */
  300.     (void) sock_udp_return_buffer(sp);
  301.     (void) sock_udp_read_ahead(sp);
  302.  
  303.     return(buflen);
  304. }
  305.  
  306.  
  307. /*
  308.  *    sock_udp_can_recv() - returns non-zero if a packet has arrived and
  309.  *                        its for us. If its not for us, does another
  310.  *                        read-ahead.
  311.  *
  312.  *                        Used by select, connect and recv.
  313.  */
  314. sock_udp_can_recv(sp)
  315.     SocketPtr sp;
  316. {
  317.     /* make the stream if its not made already */
  318.     /* .ie. first call after socket() is select() */
  319.     if (sp->sstate == SOCK_STATE_NO_STREAM)
  320.     {
  321.         int status = sock_udp_new_stream(sp);
  322.         if (status != 0)
  323.             return(-1);
  324.     }
  325.     
  326.     if (sp->apb.pb.udp.ioResult == inProgress)
  327.         return(0);
  328.     
  329.     if (sp->apb.pb.udp.ioResult == noErr &&
  330.         sp->sstate == SOCK_STATE_CONNECTED && 
  331.         (sp->peer.sin_addr.s_addr != sp->apb.pb.udp.csParam.receive.remoteHost || 
  332.         sp->peer.sin_port != sp->apb.pb.udp.csParam.receive.remotePort))
  333.     {
  334. #if SOCK_UDP_DEBUG >= 3
  335.         dprintf("sock_udp_can_recv: have %d bytes from %08x/%d\n",
  336.                 sp->apb.pb.udp.csParam.receive.rcvBuffLen,
  337.                 sp->apb.pb.udp.csParam.receive.remoteHost,
  338.                 sp->apb.pb.udp.csParam.receive.remotePort);
  339.         dprintf("sock_udp_can_recv: not from our peer - disgarding\n");
  340. #endif
  341.         (void) sock_udp_return_buffer(sp); /* sets errno */
  342.         (void) sock_udp_read_ahead(sp); /* sets errno */
  343.         return(0);
  344.     }
  345.     return(1);
  346. }
  347.  
  348.  
  349. /*
  350.  *    sock_udp_send() - send the data in the (already prepared) wds
  351.  *
  352.  *    returns bytes sent or -1 and errno
  353.  */
  354. sock_udp_send(sp, to, buffer, count, vector, flags/*ignored*/)
  355.     SocketPtr sp;
  356.     struct sockaddr_in *to;
  357.     char *buffer;
  358.     int count;
  359.     Boolean vector;
  360.     int flags;
  361. {
  362. #pragma unused(flags)
  363.     int status;
  364.     int bytes;
  365.  
  366. #if SOCK_UDP_DEBUG >= 2
  367.     dprintf("sock_udp_send: %08x state %04x\n",sp,sp->sstate);
  368. #endif
  369.  
  370.     /* make the stream if its not made already */
  371.     if (sp->sstate == SOCK_STATE_NO_STREAM)
  372.     {
  373.         status = sock_udp_new_stream(sp);
  374.         if (status != 0)
  375.             return(status);
  376.     }
  377.     
  378.     /* copy the caller's data into the wds in the write pb */
  379.     if (! vector)
  380.         bytes = sock_buf_to_wds(sp, buffer, count);
  381.     else
  382.         bytes = sock_iov_to_wds(sp, (struct iovec *)buffer, count);
  383.     if (bytes < 0)
  384.         return(-1); /* errno set */
  385.  
  386.     /* launch the packet */
  387.     if (to == NULL)
  388.         to = &sp->peer;
  389.  
  390. #if (SOCK_UDP_DEBUG >= 7) || defined(UDP_PACKET_TRACE)
  391.  
  392. #ifdef 0
  393.     hex_dump(sp->wpb[0].wds[0].ptr, sp->wpb[0].wds[0].length,
  394.             "udp to %08x/%d\n",
  395.             to->sin_addr.s_addr,to->sin_port);
  396. #endif
  397.  
  398. #endif
  399.     
  400.     (void) xUDPWrite(&sp->wpb[0].pb, to->sin_addr.s_addr,to->sin_port, &sp->wpb[0].wds[0], 0L);
  401.     switch(sp->wpb[0].pb.udp.ioResult)
  402.     {
  403.         case noErr:                 return(bytes);
  404.         case insufficientResources: return(sock_err(ENOBUFS));
  405.         case invalidLength:         return(sock_err(EMSGSIZE));
  406.         case ipDestDeadErr:            return(sock_err(EHOSTUNREACH));
  407.         case ipRouteErr:            return(sock_err(EHOSTUNREACH));
  408.         case excessCollsns:            return(sock_err(ENETDOWN));
  409.         case ipNoFragMemErr:
  410.         case ipDontFragErr:
  411.         case invalidStreamPtr:
  412.         case invalidWDS:
  413.         default:
  414.             return(sock_err(sp->wpb[0].pb.udp.ioResult));
  415.     }
  416. }
  417.  
  418. /*
  419.  *    sock_udp_can_send() - returns non-zero if a write will not block
  420.  */
  421. sock_udp_can_send(sp)
  422.     SocketPtr sp;
  423. {
  424.     /* make the stream if its not made already */
  425.     if (sp->sstate == SOCK_STATE_NO_STREAM)
  426.     {
  427.         int status = sock_udp_new_stream(sp);
  428.         if (status != 0)
  429.             return(-1);
  430.     }
  431.     
  432.     return (1);
  433. }
  434.  
  435. /*
  436.  *    sock_udp_close()
  437.  */
  438. sock_udp_close(sp)
  439.     SocketPtr sp;
  440. {
  441.     OSErr io;
  442.     
  443.     if (sp->sstate == SOCK_STATE_NO_STREAM)
  444.         return(0);
  445.     io = xUDPRelease(&sp->pb);
  446.     if (io != noErr)
  447.         return(sock_err(io));
  448.     return(0);
  449. }