211 lines
4.7 KiB
C
211 lines
4.7 KiB
C
|
/**
|
||
|
* Simple bsdsocket.library server for AmigaOS
|
||
|
* Copyright 2022 John Bintz
|
||
|
* Released under the MIT License
|
||
|
* Visit theindustriousrabbit.com for more fun!
|
||
|
*
|
||
|
* You would not believe how sparse the documentation for
|
||
|
* bsdsocket.library is on the Amiga. All this required
|
||
|
* digging through documentation on multiple sites, in
|
||
|
* multiple forum threads to describe the Amiga quirks
|
||
|
* (Amiga code has quirks? who knew!), and required a bunch of
|
||
|
* experimentation and resetting the Minimig MiSTer core
|
||
|
* and PPP connection to get this working. I tested it
|
||
|
* with CodeWatcher and nothing is leaking, so this should
|
||
|
* be a (hopefully) good bsdsocket example in C for the Amiga.
|
||
|
*
|
||
|
* This code is built for SAS/C. Be sure to disable ChkAbort
|
||
|
* in the Linker options, otherwise the SAS/C Ctrl-c
|
||
|
* handler will take over and screw up server teardown.
|
||
|
*
|
||
|
* You can use netcat/ncat on another machine to send data
|
||
|
* to the running server:
|
||
|
*
|
||
|
* echo "whoa computers" | netcat 192.168.50.100 8090
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <strings.h>
|
||
|
|
||
|
#include <proto/exec.h>
|
||
|
#include <dos/dos.h>
|
||
|
|
||
|
#include <proto/socket.h>
|
||
|
#include <arpa/inet.h>
|
||
|
#include <netinet/in.h>
|
||
|
|
||
|
// socket stuff
|
||
|
struct Library *SocketBase;
|
||
|
int serverSocket = -1;
|
||
|
struct sockaddr_in serverAddress;
|
||
|
|
||
|
#define PORT (8090)
|
||
|
#define MAX_CONNECTIONS (2)
|
||
|
|
||
|
// data handling
|
||
|
#define BUFFER_LENGTH (256)
|
||
|
char buffer [BUFFER_LENGTH];
|
||
|
int msgSize;
|
||
|
|
||
|
// set up all the global stuff we need, allowing for early exits
|
||
|
// on failures.
|
||
|
int setup(void) {
|
||
|
const int enable = 1;
|
||
|
|
||
|
// this works with the most modern MiamiDX
|
||
|
if (!(SocketBase = OpenLibrary("bsdsocket.library", 4))) {
|
||
|
printf("Can't open socket library\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
serverSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||
|
if (serverSocket < 0) {
|
||
|
printf("can't get a socket\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// AmiTCP 4.3 SDK does not have SO_REUSEPORT, this is all you get
|
||
|
if (setsockopt(
|
||
|
serverSocket,
|
||
|
SOL_SOCKET,
|
||
|
SO_REUSEADDR,
|
||
|
// oh c...
|
||
|
&enable,
|
||
|
sizeof(int)
|
||
|
) < 0) {
|
||
|
printf("can't set socket options\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void teardown(void) {
|
||
|
if (serverSocket != -1) {
|
||
|
|
||
|
shutdown(
|
||
|
serverSocket,
|
||
|
// SHUT_RDWR, stop all communication both ways
|
||
|
2
|
||
|
);
|
||
|
// double close it to stop the listen as well
|
||
|
CloseSocket(serverSocket);
|
||
|
CloseSocket(serverSocket);
|
||
|
}
|
||
|
|
||
|
if (SocketBase) {
|
||
|
CloseLibrary(SocketBase);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int startServer(void) {
|
||
|
// bind to all addresses on our defined port
|
||
|
memset(&serverAddress, 0, sizeof(serverAddress));
|
||
|
serverAddress.sin_family = AF_INET;
|
||
|
serverAddress.sin_port = htons(PORT);
|
||
|
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||
|
|
||
|
if (bind(
|
||
|
serverSocket,
|
||
|
(struct sockaddr *) &serverAddress,
|
||
|
sizeof(serverAddress)
|
||
|
) < 0) {
|
||
|
printf("can't bind\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (listen(serverSocket, MAX_CONNECTIONS) < 0) {
|
||
|
printf("can't listen\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void readData(int clientSocket) {
|
||
|
do {
|
||
|
// keep stomping over the same buffer
|
||
|
// ideally we'd take the data out of the buffer and
|
||
|
// do something with it.
|
||
|
msgSize = recv(clientSocket, buffer, BUFFER_LENGTH, 0);
|
||
|
|
||
|
if (msgSize <= 0) {
|
||
|
printf("End of data\n");
|
||
|
} else {
|
||
|
printf("Got stuff: %i\n", msgSize);
|
||
|
printf("%s\n", buffer);
|
||
|
}
|
||
|
} while (msgSize == 256);
|
||
|
printf("Data received: %s\n", buffer);
|
||
|
}
|
||
|
|
||
|
int main(void) {
|
||
|
// The incoming netcat client
|
||
|
int clientSocket;
|
||
|
struct sockaddr_in clientAddress;
|
||
|
LONG clientLength = sizeof(clientAddress);
|
||
|
|
||
|
// be able to use WaitSelect
|
||
|
int waitSelectResult;
|
||
|
fd_set readFDs;
|
||
|
|
||
|
if (setup() != 0) {
|
||
|
teardown();
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
printf("got socket number %d\n", serverSocket);
|
||
|
|
||
|
if (startServer() != 0) {
|
||
|
teardown();
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
printf("listening on port %d\n", PORT);
|
||
|
|
||
|
// just enough to get WaitSelect working
|
||
|
// this macro also wants strings.h included
|
||
|
FD_ZERO(&readFDs); ((
|
||
|
FD_SET(serverSocket, &readFDs);
|
||
|
waitSelectResult = WaitSelect(
|
||
|
serverSocket + 1,
|
||
|
&readFDs,
|
||
|
NULL, NULL, NULL,
|
||
|
// other Exec Signals would go here as ORed mp_SigBits
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
// WaitSelect will trap on Ctrl-C for us, and returns -1 if that happens
|
||
|
if (waitSelectResult == -1) {
|
||
|
printf("ctrl-c\n");
|
||
|
teardown();
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
if (
|
||
|
(
|
||
|
clientSocket = accept(
|
||
|
serverSocket,
|
||
|
(struct sockaddr *) &clientAddress,
|
||
|
&clientLength
|
||
|
)
|
||
|
) < 0
|
||
|
) {
|
||
|
printf("can't accept\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// note the capitalization difference on Inet_NtoA. the Amiga does
|
||
|
// that a lot...
|
||
|
printf("connection from %s\n", Inet_NtoA(clientAddress.sin_addr.s_addr));
|
||
|
|
||
|
readData(clientSocket);
|
||
|
|
||
|
printf("all done\n");
|
||
|
|
||
|
teardown();
|
||
|
|
||
|
return 0;
|
||
|
}
|