Aros/Developer/Docs/Libraries/BSDsocket

Navbar for the Aros wikibook
Aros User Docs
Aros User Docs
Aros User FAQs
Aros User Applications
Aros User DOS Shell
Aros/User/AmigaLegacy
Aros Dev Docs
Aros Developer Docs
Porting Software from AmigaOS/SDL
For Zune Beginners
Zune .MUI Classes
For SDL Beginners
Aros Developer BuildSystem
Specific platforms
68k Support
PPC Power Architecture Support
Arm Raspberry Pi Support
Android support
Linux and FreeBSD Support
Windows Mingw and MacOSX Support
Aros x86 Installing
Aros x86 Audio/Video Support
Aros x86 Network Support
Aros x86 Complete System HCL
Aros Storage Support IDE SATA etc
Aros Poseidon USB Support
x86-64 Support
misc
Aros Public License


IntroductionEdit

Amiga had a few attempts at internet access (TCP) and address (IP) stacks. AmiTCP (BSD 4.3) to Miami ( ) to Roadshow (OS4 and possibly AmigaOS (TM)).

A few of these functions have macros for easier use (select, inet_ntoa, etc.), but you must use CloseSocket and IoctlSocket in place of close and ioctl. Bsdsocket.library has it's own ernno, which can be queried with Errno() or you can make it use the errno variable using SetErrnoPtr() or SocketBaseTagList()

Make sure the bsdsocket.library is opened before you call the bsd functions, if not than its very normal that is where it crashes...


net_udp.c contains...

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <errno.h>

#include <proto/socket.h> 
#include <sys/socket.h> 
#include <bsdsocket/socketbasetags.h>

#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>


#ifdef __AROS__
#include proto/bsdsocket.h
#endif

#ifdef NeXT
#include libc.h
#endif

and uncomment them

//extern int gethostname (char *, int);
//extern int close (int);


For example you create a socket with socket() (from bsdsocket.library) and then you pass it to write() from arosc, but this integer value means something different to arosc. Same with other way around, using fd created by arosc with bsdsocket.library.

Do you know that bsdsocket API has support for fd hooks. They allow to sync up fd numbers between bsdsocket.library and libc. If you look at original netlib code, you'll see it. That implementation is based on some support from SAS/C libc.

#include <proto/exec.h>
struct Library * SocketBase = NULL;
int h_errno = 0;
extern int errno;

and then in the initialization code:

if(!(SocketBase = OpenLibrary("bsdsocket.library", 4 ))) {
SDLNet_SetError("No TCP/IP Stack running!\n");
return(-1);
}

if( SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (IPTR)&errno,
SBTM_SETVAL(SBTC_HERRNOLONGPTR), (IPTR)&h_errno, TAG_DONE )) {
SDLNet_SetError("Error initializing bsdsocket\n");
return(-1);
}


  • Under AROS you need -larossupport instead of -ldebug.
  • Of course you need -lsdl since you are using SDL :)
  • make sure ioctl or ioctlsocket is defined to IoctlSocket, and close to CloseSocket.


ReferenceEdit

Every bsdsocket.library call takes SocketBase as an argument, and since you declared a global NULL SocketBase socket functions will fail. To solve the problem, you can include the header blow instead of proto/bsdsocket.h, and use SocketBase as normal, which will become the taks's user defined field. Please note that you have to do call init_arossock() in the right thread, preferably in a wrapper around exec_list, and don't declare a global SocketBase variable.

#ifndef TASKSOCKBASE_H
#define TASKSOCKBASE_H

/*
 * Per-task socketbase using tc_UserData
 */

#include <proto/exec.h>

#define SocketBase FindTask(NULL)->tc_UserData
#define __BSDSOCKET_NOLIBBASE__
#include <proto/bsdsocket.h>

#endif



As far as i know WaitSelect() works just like select() and would be nice to paste bit of the code here where it fails... WaitSelect can only be used on sockets, not on DOS filehandles. On unix they are handled uniformly, but on Amiga you have to use different code path for file handles and sockets.

sys/select.h
---------------
#ifndef select(nfds,rfds,wfds,efds,timeout)
#define select(nfds,rfds,wfds,efds,timeout) WaitSelect(nfds,rfds,wfds,efds,timeout,NULL)
#endif


and the test the select

struct timeval timeout;
int socket_d;
struct fd_set client_d;


//set to 3 mins
timeout.tv_sec = 3 * 60;
timeout.tv_usec = 0;

do
{

rc = select(socket_d + 1, &client_d, NULL, NULL, &timeout);


/* Check to see if the select call failed. */
if (rc < 0)
{
perror(" select() failed");
break;
}


/* Check to see if the 3 minute time out expired. */
if (rc == 0)
{
printf(" select() timed out. End program.\n");
break;
}
}


One of the arguments of LockMutex is still NULL, it could be ThreadBase too. One thing I noticed is that you don't use pthread_join/WaitThread in your code. Instead you do busy loops until the given thread sets its state. Because one of the functions which overflows the stack is wait_thread_running().

LockMutex fails in exe_elist(), because cclist_mutex is not itialized for AROS in main.c, hence the NULL pointer.

Initializing cclist_mutex solves the problem. Here are the two thread-waiting functions without the busy loop:

void wait_thread_running (S4 threadnum)
{
    LockMutex (pthreads_mutex);
    if (pthreads[threadnum].thread)
        WaitThread(pthreads[threadnum].thread, NULL);
    UnlockMutex (pthreads_mutex);
}

void join_threads (S4 threadnum)
{
    /* wait until all threads are stopped */
    S4 i;
    
    LockMutex (pthreads_mutex);
    /* thread zero is the main thread, don't wait for it!!! */
    for (i = 1; i < MAXPTHREADS; i++)
    {
        /* don't check the calling thread!!! */
        if (i != threadnum && pthreads[i].thread)
        {
            WaitThread(pthreads[i].thread, NULL);
        }
    }
    UnlockMutex (pthreads_mutex);
}

Replacing WaitThread with pthread_join would work on other platforms. Checking (pthreads[i].state == PTHREAD_RUNNING) isn't necessary anymore, since these thread waiting functions instantly return on terminated threads.

It's waiting till the thread is started (was created). It's the thread_sync function in the examples. This is for synchronizing threads. The next step would be sendig data to the thread: with threadpush ().

Also make sure that to open bsdsocket.library in the same thread where you do the socket operations, because you can't share socket descriptors between threads. Technically it's possible to pass sds between threads, but it requires some extra code, and generally not worth the hassle if you can move all the socket manipulation into one thread.



ExamplesEdit

/*
Copyright (C) 2008-2009 Mark Olsen

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>

#ifdef __MORPHOS__
#include <sys/filio.h>
#elif defined(AROS)
#include <sys/ioctl.h>
#endif

#include <devices/timer.h>

#include <proto/exec.h>
#include <proto/socket.h>

#include <string.h>

#include "quakedef.h"
#include "sys_net.h"

struct SysSocket
{
        int s;
};

struct SysNetData
{
        struct Library *SocketBase;
        struct MsgPort *timerport;
        struct timerequest *timerrequest;
};

#define SocketBase netdata->SocketBase

struct SysNetData *Sys_Net_Init()
{
        struct SysNetData *netdata;

        netdata = AllocVec(sizeof(*netdata), MEMF_ANY);
        if (netdata)
        {
                SocketBase = OpenLibrary("bsdsocket.library", 0);
                if (SocketBase)
                {
                        netdata->timerport = CreateMsgPort();
                        if (netdata->timerport)
                        {
                                netdata->timerrequest = (struct timerequest *)CreateIORequest(netdata->timerport, sizeof(*netdata->timerrequest));
                                if (netdata->timerrequest)
                                {
                                        if (OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)netdata->timerrequest, 0) == 0)
                                        {
                                                netdata->timerrequest->tr_node.io_Command = TR_ADDREQUEST;
                                                netdata->timerrequest->tr_time.tv_secs = 1;
                                                netdata->timerrequest->tr_time.tv_micro = 0;
                                                SendIO((struct IORequest *)netdata->timerrequest);
                                                AbortIO((struct IORequest *)netdata->timerrequest);

                                                return netdata;
                                        }

                                        DeleteIORequest((struct IORequest *)netdata->timerrequest);
                                }

                                DeleteMsgPort(netdata->timerport);
                        }

                        CloseLibrary(SocketBase);
                }

                FreeVec(netdata);
        }

        return 0;
}

void Sys_Net_Shutdown(struct SysNetData *netdata)
{
        WaitIO((struct IORequest *)netdata->timerrequest);
        CloseDevice((struct IORequest *)netdata->timerrequest);
        DeleteIORequest((struct IORequest *)netdata->timerrequest);
        DeleteMsgPort(netdata->timerport);
        CloseLibrary(SocketBase);
        FreeVec(netdata);
}

qboolean Sys_Net_ResolveName(struct SysNetData *netdata, const char *name, struct netaddr *address)
{
        struct hostent *remote;

        remote = gethostbyname(name);
        if (remote)
        {
                address->type = NA_IPV4;
                *(unsigned int *)address->addr.ipv4.address = *(unsigned int *)remote->h_addr;

                return true;
        }

        return false;
}

qboolean Sys_Net_ResolveAddress(struct SysNetData *netdata, const struct netaddr *address, char *output, unsigned int outputsize)
{
        struct hostent *remote;
        struct in_addr addr;

        if (address->type != NA_IPV4)
                return false;

        addr.s_addr = (address->addr.ipv4.address[0]<<24)|(address->addr.ipv4.address[1]<<16)|(address->addr.ipv4.address[2]<<8)|address->addr.ipv4.address[3];

        remote = gethostbyaddr((void *)&addr, sizeof(addr), AF_INET);
        if (remote)
        {
                strlcpy(output, remote->h_name, outputsize);

                return true;
        }

        return false;
}

struct SysSocket *Sys_Net_CreateSocket(struct SysNetData *netdata, enum netaddrtype addrtype)
{
        struct SysSocket *s;
        int r;
        int one;

        one = 1;

        if (addrtype != NA_IPV4)
                return 0;

        s = AllocVec(sizeof(*s), MEMF_ANY);
        if (s)
        {
                s->s = socket(AF_INET, SOCK_DGRAM, 0);
                if (s->s != -1)
                {
                        r = IoctlSocket(s->s, FIONBIO, (void *)&one);
                        if (r == 0)
                        {
                                return s;
                        }

                        CloseSocket(s->s);
                }

                FreeVec(s);
        }

        return 0;
}

void Sys_Net_DeleteSocket(struct SysNetData *netdata, struct SysSocket *socket)
{
        CloseSocket(socket->s);
        FreeVec(socket);
}

qboolean Sys_Net_Bind(struct SysNetData *netdata, struct SysSocket *socket, unsigned short port)
{
        int r;
        struct sockaddr_in addr;

        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        *(unsigned int *)&addr.sin_addr.s_addr = 0;

        r = bind(socket->s, (struct sockaddr *)&addr, sizeof(addr));
        if (r == 0)
                return true;

        return false;
}

int Sys_Net_Send(struct SysNetData *netdata, struct SysSocket *socket, const void *data, int datalen, const struct netaddr *address)
{
        int r;

        if (address)
        {
                struct sockaddr_in addr;

                addr.sin_family = AF_INET;
                addr.sin_port = htons(address->addr.ipv4.port);
                *(unsigned int *)&addr.sin_addr.s_addr = *(unsigned int *)address->addr.ipv4.address;

                r = sendto(socket->s, data, datalen, 0, (struct sockaddr *)&addr, sizeof(addr));
        }
        else
                r = send(socket->s, data, datalen, 0);

        if (r == -1)
        {
                if (Errno() == EWOULDBLOCK)
                        return 0;
        }

        return r;
}

int Sys_Net_Receive(struct SysNetData *netdata, struct SysSocket *socket, void *data, int datalen, struct netaddr *address)
{
        int r;

        if (address)
        {
                LONG fromlen;
                struct sockaddr_in addr;

                fromlen = sizeof(addr);

                r = recvfrom(socket->s, data, datalen, 0, (struct sockaddr *)&addr, &fromlen);

                if (fromlen != sizeof(addr))
                        return -1;

                address->type = NA_IPV4;
                address->addr.ipv4.port = htons(addr.sin_port);
                *(unsigned int *)address->addr.ipv4.address = *(unsigned int *)&addr.sin_addr.s_addr;
        }
        else
                r = recv(socket->s, data, datalen, 0);

        if (r == -1)
        {
                if (Errno() == EWOULDBLOCK)
                        return 0;
        }

        return r;
}

void Sys_Net_Wait(struct SysNetData *netdata, struct SysSocket *socket, unsigned int timeout_us)
{
        fd_set rfds;
        ULONG sigmask;

        WaitIO((struct IORequest *)netdata->timerrequest);

        if (SetSignal(0, 0) & (1<<netdata->timerport->mp_SigBit))
                Wait(1<<netdata->timerport->mp_SigBit);

        FD_ZERO(&rfds);
        FD_SET(socket->s, &rfds);

        netdata->timerrequest->tr_node.io_Command = TR_ADDREQUEST;
        netdata->timerrequest->tr_time.tv_secs = timeout_us / 1000000;
        netdata->timerrequest->tr_time.tv_micro = timeout_us % 1000000;

        SendIO((struct IORequest *)netdata->timerrequest);

        sigmask = 1<<netdata->timerport->mp_SigBit;

        WaitSelect(socket->s + 1, &rfds, 0, 0, 0, &sigmask);

        AbortIO((struct IORequest *)netdata->timerrequest);

}




ReferenceEdit

The first is about breaking some api level functionality to make way for supporting IPv6. Would it be a great deal to fix the existing functions that assume IPv4 addresses, so they take generic structures instead? miami.library provides the missing API. Miami was going the same way those back then. IPv6 looks like a failed experiment. No one is seriously deploying it. In fact not much needs to be replaced. It affects mainly resolver, introducing inet_PtoN() and inet_NtoP(). The rest is perfectly handled by existing socket API. Just add AF_INET6.

How big a task would it be to replace the existing bsd-based internals of AROSTCP with more upto date code? Take recent NetBSD and you're done. FreeBSD diverted a lot. This will give you working sysctl and BPF/PCAP. AROSTCP already has some upgraded code. Its codebase is NetBSD v2.0.5. BTW, userland (like resolver) from FreeBSD perfectly fits in. One more idea: separate protocols into loadable modules. This would give you AF_UNIX, AF_BT, etc. There is a single-linked list where all protocols are registered. Nodes are looked up using AF_xxx as a key. Then there are several pointers to functions. That's all. This is very well separate-able. In the end you would have protocol modules with these functions exported, and something like bsdcore.library, implementing basic lowlevel stuff like mbufs and bsd_malloc(). Ah, yes, there is some remainder (IIRC) from original BSD4.3, where AF_xxx are indexes into array. I was lazy enough not to remove this. So, for now both lookup mechanisms are coexisting. Throw away the old one, use NetBSD code as a reference. In fact BSD is very modular at source code level, the same as Linux. You can switch on and off any part without affecting others. So, porting the code isn't really difficult.


Support of non-blocking sockets is added so that we can finally port e.g. some torrent clients.


Would like to propose a 'unified file descriptor' model for arosc.library, to simplify porting of POSIX applications. The goal is to be able to use the same 'int fd' type to represent both the DOS BPTR filehandles and bsdsocket.library socket backend. This will eliminate the need to rewrite code to use send() instead of write() to sockets, and call CloseSocket() instead of close() for sockets.

Implementation would be as a table (indexed by fd #) that would have the following struct:

struct arosc_fd {
  APTR file;
  struct arosc_fd_operation {

      /* Required for all FDs
        */
      int (*close)(APTR file);
      LONG (*sigmask)(APTR file); /* Gets the IO signal mask for poll(2)ing */
      ssize_t (*write)(APTR file, const void *buff, size_t len);
      ssize_t (*read)(APTR file, void *buff, size_t len);
      ..etc etc...

      /* The following are optional operations
        */
      off_t (*lseek)(APTR file, off_t offset, int whence);
      ssize_t (*sendto)(APTR file, const void *buff, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
      ssize_t (*recvfrom)(APTR file, void *buff, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
      ssize_t (*sendmsg)(APTR file, const struct msghdr *msg, int flags);
      ssize_t (*recvmsg)(APTR file, struct msghdr *msg, int flags);
      ..etc etc..
  } *op;
} *fd_table;

The C library routines 'open()' and 'socket()' would be modified to:

  • Ensure that either dos.library or bsdsocket.library is opened.
  • Allocate a new fd in the FD table
  • Set fd_table[fd].ops to a 'struct arosc_fd_operation' pointer, that defines the IO operations for either dos.library for bsdsocket.library
  • Set fd_table[fd].file to a pointer to the private data for those operation (ie BPTR or socket fd #)
  • Return the new FD number

The read()/write()/etc. would be modified to use the fd_table[fd].op routines to perform IO

The poll() routine will wait for activity on the OR of all the signal masks of the the provided FDs.

The close() routine also deallocates the arosc_fd entry


It's a layer on top of it. bsdsocket.library would be opened dynamically from arosc.library's socket() routine if needed.

since arosc generated FD's will be different from bsdsocket.library ones how will programmers know what to pass to functions? Programmers would not use the bsdsocket headers nor protos directly (similar arosc.library headers would be generated that wrap them). The point is that programmers would no longer need to treat bsdsocket fd as a special case.

the whole fd_table[] mapping concept. The 'file' element of the fd_table[n] would be a BPTR for a open()ed fd, and a bsdsocket.library socket descriptor number for one created by arosc's socket() call. The user would *not* be calling bsdsocket.library directly under the proposed scheme. Everything in that library would be wrapped by arosc.library.

make sure that those codes will continue using bsdsocket functions and won't switch half arosc / half bsdsocket (for example arosc->send and bsdsocket->CloseLibrary). The 'best' solution would be to have a compilation flag (ie

'#define BSDSOCKET_API') that ifdefs out the arosc send()/recv()/socket()/etc prototypes and makes send()/recv()/CloseSocket() and friends from the bsdsocket protos visible.

If BSDSOCKET_API is not defined, but bsdsocket.library is intended (ie calls to CloseSocket(), etc), then the user will get compilation errors about missing function prototypes. If BSDSOCKET_API is defined, but POSIX is intended, then they will get a link error about 'undefined symbol SocketLibrary'

Maybe the bsdsocket includes should also have some detection of arosc includes and produce the warning about the BSDSOCKET_API define beeing accessible to developers if both headers are included in the same compilation unit - I'm just wondering how we are going to get the word out to 3rd party developers that they don't have to change their codes and can simply use the define - or maybe it should be the other way around? bsdsocket by default and POSIX_SOCKET_API define to enable the support you mention?

Last modified on 16 January 2013, at 03:59