mirror of
https://github.com/espruino/Espruino.git
synced 2025-12-08 19:06:15 +00:00
233 lines
6.7 KiB
C
233 lines
6.7 KiB
C
/*
|
|
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
|
|
*
|
|
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
* Implementation of JsNetwork for Linux
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
#include "network.h"
|
|
#include "network_linux.h"
|
|
|
|
#include <string.h> // for memset
|
|
|
|
#define INVALID_SOCKET ((SOCKET)(-1))
|
|
#define SOCKET_ERROR (-1)
|
|
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#ifdef WIN32
|
|
#include <winsock.h>
|
|
#else
|
|
#ifndef ESP_PLATFORM
|
|
#include <sys/select.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <resolv.h>
|
|
#endif
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
|
|
typedef struct sockaddr_in sockaddr_in;
|
|
typedef int SOCKET;
|
|
#endif
|
|
|
|
#define closesocket(SOCK) close(SOCK)
|
|
|
|
|
|
/// Get an IP address from a name. Sets out_ip_addr to 0 on failure
|
|
void net_linux_gethostbyname(JsNetwork *net, char * hostName, uint32_t* out_ip_addr) {
|
|
NOT_USED(net);
|
|
struct hostent * host_addr_p = gethostbyname(hostName);
|
|
if (host_addr_p)
|
|
*out_ip_addr = *(uint32_t*)*host_addr_p->h_addr_list;
|
|
}
|
|
|
|
/// Called on idle. Do any checks required for this device
|
|
void net_linux_idle(JsNetwork *net) {
|
|
NOT_USED(net);
|
|
}
|
|
|
|
/// Call just before returning to idle loop. This checks for errors and tries to recover. Returns true if no errors.
|
|
bool net_linux_checkError(JsNetwork *net) {
|
|
NOT_USED(net);
|
|
bool hadErrors = false;
|
|
return hadErrors;
|
|
}
|
|
|
|
/// if host=0, creates a server otherwise creates a client (and automatically connects). Returns >=0 on success
|
|
int net_linux_createsocket(JsNetwork *net, uint32_t host, unsigned short port, SocketType socketType) {
|
|
NOT_USED(net);
|
|
int sckt = -1;
|
|
if (host!=0) { // ------------------------------------------------- host (=client)
|
|
sockaddr_in sin;
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = htons( port );
|
|
|
|
sckt = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (sckt<0) return sckt; // error
|
|
|
|
// turn on non-blocking mode
|
|
#ifdef WIN_OS
|
|
u_long n = 1;
|
|
ioctlsocket(sckt,FIONBIO,&n);
|
|
#endif
|
|
|
|
sin.sin_addr.s_addr = (in_addr_t)host;
|
|
|
|
int res = connect(sckt,(struct sockaddr *)&sin, sizeof(sockaddr_in) );
|
|
|
|
if (res == SOCKET_ERROR) {
|
|
#ifdef WIN_OS
|
|
int err = WSAGetLastError();
|
|
#else
|
|
int err = errno;
|
|
#endif
|
|
if (err != EINPROGRESS &&
|
|
err != EWOULDBLOCK) {
|
|
jsError("Connect failed (err %d)\n", err );
|
|
closesocket(sckt);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
} else { // ------------------------------------------------- no host (=server)
|
|
|
|
sckt = socket(AF_INET, // Go over TCP/IP
|
|
SOCK_STREAM, // This is a stream-oriented socket
|
|
IPPROTO_TCP); // Use TCP rather than UDP
|
|
if (sckt == INVALID_SOCKET) {
|
|
jsError("Socket creation failed");
|
|
return 0;
|
|
}
|
|
int optval = 1;
|
|
if (setsockopt(sckt,SOL_SOCKET,SO_REUSEADDR,(const char *)&optval,sizeof(optval)) < 0)
|
|
jsWarn("setsockopt(SO_REUSADDR) failed\n");
|
|
|
|
int nret;
|
|
sockaddr_in serverInfo;
|
|
memset(&serverInfo, 0, sizeof(serverInfo));
|
|
serverInfo.sin_family = AF_INET;
|
|
//serverInfo.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // allow only LOCAL clients to connect
|
|
serverInfo.sin_addr.s_addr = INADDR_ANY; // allow anyone to connect
|
|
serverInfo.sin_port = (unsigned short)htons((unsigned short)port); // port
|
|
nret = bind(sckt, (struct sockaddr*)&serverInfo, sizeof(serverInfo));
|
|
if (nret == SOCKET_ERROR) {
|
|
jsError("Socket bind failed");
|
|
closesocket(sckt);
|
|
return -1;
|
|
}
|
|
|
|
// Make the socket listen
|
|
nret = listen(sckt, 10); // 10 connections (but this ignored on CC30000)
|
|
if (nret == SOCKET_ERROR) {
|
|
jsError("Socket listen failed");
|
|
closesocket(sckt);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#ifdef SO_NOSIGPIPE
|
|
// disable SIGPIPE
|
|
int optval = 1;
|
|
if (setsockopt(sckt,SOL_SOCKET,SO_NOSIGPIPE,(const char *)&optval,sizeof(optval))<0)
|
|
jsWarn("setsockopt(SO_NOSIGPIPE) failed\n");
|
|
#endif
|
|
|
|
return sckt;
|
|
}
|
|
|
|
/// destroys the given socket
|
|
void net_linux_closesocket(JsNetwork *net, int sckt) {
|
|
NOT_USED(net);
|
|
closesocket(sckt);
|
|
}
|
|
|
|
/// If the given server socket can accept a connection, return it (or return < 0)
|
|
int net_linux_accept(JsNetwork *net, int sckt) {
|
|
NOT_USED(net);
|
|
// TODO: look for unreffed servers?
|
|
fd_set s;
|
|
FD_ZERO(&s);
|
|
FD_SET(sckt,&s);
|
|
// check for waiting clients
|
|
struct timeval timeout;
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 0;
|
|
int n = select(sckt+1,&s,NULL,NULL,&timeout);
|
|
if (n>0) {
|
|
// we have a client waiting to connect... try to connect and see what happens
|
|
int theClient = accept(sckt,0,0);
|
|
return theClient;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/// Receive data if possible. returns nBytes on success, 0 on no data, or -1 on failure
|
|
int net_linux_recv(JsNetwork *net, int sckt, void *buf, size_t len) {
|
|
NOT_USED(net);
|
|
int num = 0;
|
|
fd_set s;
|
|
FD_ZERO(&s);
|
|
FD_SET(sckt,&s);
|
|
// check for waiting clients
|
|
struct timeval timeout;
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 0;
|
|
int n = select(sckt+1,&s,NULL,NULL,&timeout);
|
|
if (n==SOCKET_ERROR) {
|
|
// we probably disconnected
|
|
return -1;
|
|
} else if (n>0) {
|
|
// receive data
|
|
num = (int)recv(sckt,buf,len,0);
|
|
if (num==0) num=-1; // select says data, but recv says 0 means connection is closed
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
/// Send data if possible. returns nBytes on success, 0 on no data, or -1 on failure
|
|
int net_linux_send(JsNetwork *net, int sckt, const void *buf, size_t len) {
|
|
NOT_USED(net);
|
|
fd_set writefds;
|
|
FD_ZERO(&writefds);
|
|
FD_SET(sckt, &writefds);
|
|
struct timeval time;
|
|
time.tv_sec = 0;
|
|
time.tv_usec = 0;
|
|
int n = select(sckt+1, 0, &writefds, 0, &time);
|
|
if (n==SOCKET_ERROR ) {
|
|
// we probably disconnected so just get rid of this
|
|
return -1;
|
|
} else if (FD_ISSET(sckt, &writefds)) {
|
|
int flags = 0;
|
|
#if !defined(SO_NOSIGPIPE) && defined(MSG_NOSIGNAL)
|
|
flags |= MSG_NOSIGNAL;
|
|
#endif
|
|
n = (int)send(sckt, buf, len, flags);
|
|
return n;
|
|
} else
|
|
return 0; // just not ready
|
|
}
|
|
|
|
void netSetCallbacks_linux(JsNetwork *net) {
|
|
net->idle = net_linux_idle;
|
|
net->checkError = net_linux_checkError;
|
|
net->createsocket = net_linux_createsocket;
|
|
net->closesocket = net_linux_closesocket;
|
|
net->accept = net_linux_accept;
|
|
net->gethostbyname = net_linux_gethostbyname;
|
|
net->recv = net_linux_recv;
|
|
net->send = net_linux_send;
|
|
net->chunkSize = 536;
|
|
}
|