Initial import of Dillo

This commit is contained in:
2025-02-28 13:34:30 -05:00
parent bd4e3eebd8
commit 20fea64cb5
496 changed files with 156174 additions and 0 deletions

36
dpid/Makefile.am Normal file
View File

@ -0,0 +1,36 @@
AM_CPPFLAGS = \
-I$(top_srcdir) \
-DEXEEXT='"$(EXEEXT)"' \
-DDPIDRC_SYS='"$(sysconfdir)/dpidrc"'
bin_PROGRAMS = dpid dpidc
dpid_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
dpidc_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
EXTRA_DIST = dpidrc.in
dpid_SOURCES = \
dpi.h \
dpi_socket_dir.h \
dpid.h \
dpid_common.h \
misc_new.h \
dpi.c \
dpi_socket_dir.c \
dpid.c \
dpid_common.c \
main.c \
misc_new.c
dpidc_SOURCES = dpidc.c
sysconf_DATA = dpidrc
CLEANFILES = $(sysconf_DATA)
dpidrc: $(srcdir)/dpidrc.in Makefile
sed -e 's|[@]libdir[@]|$(libdir)|;s|[@]EXEEXT[@]|$(EXEEXT)|g' $(srcdir)/dpidrc.in > dpidrc

32
dpid/TODO Normal file
View File

@ -0,0 +1,32 @@
Todo List
File dpi_service.c
This module should be removed because its original functions
have been removed or modified. Put these functions in dpid.c
File dpi_service.h
This module should be removed because its original functions
have been removed or modified. Put these functions in dpid.c
Add other file types, but first we need to add files associated
with a dpi to the design.
Global SRS_NAME
Should read this from dillorc
File dpid_common.h
The dpid error codes will be used in the next patch
Global main()
+ add new plugins when they become available
__________________________________________________________
Remove global variables.
Use a singly linked list for dpi_attr_list
Allow dpis to be registered one at a time
Only run dpis listed in users dillorc
MAYBE Have dpid accept all connections to dpis (fixes inf loop if dpi crashes before accept)

97
dpid/dpi.c Normal file
View File

@ -0,0 +1,97 @@
/*
Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
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 3 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, see <http://www.gnu.org/licenses/>.
*/
/*! \file
* Access functions for ~/.dillo/dpi_socket_dir.
* The most useful function for dillo is a_Dpi_srs, it returns
* the full path to the dpid service request socket.
*/
#include <errno.h>
#include <stdlib.h> /* for exit */
#include "dpid_common.h"
#include "dpi.h"
#include "misc_new.h"
/*! \Return
* Returns path to the dpi_socket_dir file
* Use dFree to free memory
*/
char *a_Dpi_sockdir_file(void)
{
char *dpi_socket_dir, *dirfile_path = "/.dillo/dpi_socket_dir";
dpi_socket_dir = dStrconcat(dGethomedir(), dirfile_path, NULL);
return dpi_socket_dir;
}
/*! Read socket directory path from ~/.dillo/dpi_socket_dir
* \Return
* socket directory path or NULL if the dpi_socket_dir file does not exist.
* \Note
* This function exits if ~/.dillo does not exist or
* if the dpi_socket_dir file cannot be opened for a
* reason other than it does not exist.
*/
char *a_Dpi_rd_dpi_socket_dir(char *dirname)
{
FILE *dir;
char *sockdir = NULL, *rcpath;
rcpath = dStrconcat(dGethomedir(), "/.dillo", NULL);
/* If .dillo does not exist it is an unrecoverable error */
if (access(rcpath, F_OK) == -1) {
ERRMSG("a_Dpi_rd_dpi_socket_dir", "access", errno);
MSG_ERR(" - %s\n", rcpath);
exit(1);
}
dFree(rcpath);
if ((dir = fopen(dirname, "r")) != NULL) {
sockdir = dGetline(dir);
fclose(dir);
} else if (errno == ENOENT) {
ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno);
MSG_ERR(" - %s\n", dirname);
} else if (errno != ENOENT) {
ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno);
MSG_ERR(" - %s\n", dirname);
exit(1);
}
return sockdir;
}
/*!
* \Modifies
* srs_name
* \Return
* The service request socket name with its complete path.
*/
char *a_Dpi_srs(void)
{
char *dirfile_path, *sockdir, *srs_name;
dirfile_path = a_Dpi_sockdir_file();
sockdir = dStrstrip(a_Dpi_rd_dpi_socket_dir(dirfile_path));
srs_name = dStrconcat(sockdir, "/", "dpid.srs", NULL);
dFree(sockdir);
dFree(dirfile_path);
return (srs_name);
}

45
dpid/dpi.h Normal file
View File

@ -0,0 +1,45 @@
/*! \file
* Access functions for ~/.dillo/dpi_socket_dir.
* The most useful function for dillo is a_Dpi_srs, it returns
* the full path to the dpid service request socket.
*/
#ifndef DPI_H
#define DPI_H
#include <unistd.h> /* for socklen_t */
#include <sys/socket.h> /* for socklen_t and AF_LOCAL */
/* Some systems may not have this one... */
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif
/* This one is tricky, some sources state it should include the byte
* for the terminating NULL, and others say it shouldn't.
* The other way is to only use this one when a native SUN_LEN is not present,
* but as dillo has used this for a long time successfully, here it goes.
*/
# define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ strlen ((ptr)->sun_path))
/*!
* dpi commands
*/
enum {
UNKNOWN_CMD,
AUTH_CMD, /* authentication */
BYE_CMD, /* "DpiBye" */
CHECK_SERVER_CMD, /* "check_server" */
REGISTER_ALL_CMD, /* "register_all" */
REGISTER_SERVICE_CMD /* "register_service" */
};
char *a_Dpi_sockdir_file(void);
char *a_Dpi_rd_dpi_socket_dir(char *dirname);
char *a_Dpi_srs(void);
#endif

129
dpid/dpi_socket_dir.c Normal file
View File

@ -0,0 +1,129 @@
/*
Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
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 3 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, see <http://www.gnu.org/licenses/>.
*/
/*! \file
* Create a per user temporary directory for dpi sockets
*/
#include <errno.h>
#include <stdlib.h>
#include "dpid_common.h"
#include "dpi.h"
#include "misc_new.h"
#include "dpi_socket_dir.h" /* for function prototypes */
/*! Save socket directory name in ~/.dillo/dpi_socket_dir
* \Return
* \li 1 on success
* \li -1 on failure
*/
int w_dpi_socket_dir(char *dirname, char *sockdir)
{
FILE *dir;
if ((dir = fopen(dirname, "w")) == NULL) {
ERRMSG("w_dpi_socket_dir", "fopen", errno);
return (-1);
}
fprintf(dir, "%s", sockdir);
fclose(dir);
return (1);
}
/*! Test that socket directory exists and is a directory
* \Return
* \li 1 on success
* \li 0 on failure
* \bug Does not check permissions or that it's a symbolic link
* to another directory.
*/
int tst_dir(char *dir)
{
char *dirtest;
int ret = 0;
/* test for a directory */
dirtest = dStrconcat(dir, "/", NULL);
if (access(dirtest, F_OK) == -1) {
ERRMSG("tst_dir", "access", errno);
MSG_ERR(" - %s\n", dirtest);
} else {
ret = 1;
}
dFree(dirtest);
return ret;
}
/*! Create socket directory
* \Return
* \li Socket directory path on success
* \li NULL on failure
*/
char *mk_sockdir(void)
{
char *template, *logname;
logname = getenv("LOGNAME") ? getenv("LOGNAME") : "dillo";
template = dStrconcat("/tmp/", logname, "-", "XXXXXX", NULL);
if (a_Misc_mkdtemp(template) == NULL) {
ERRMSG("mk_sockdir", "a_Misc_mkdtemp", 0);
MSG_ERR(" - %s\n", template);
dFree(template);
return (NULL);
}
return template;
}
/*! Create socket directory if it does not exist and save its name in
* ~/.dillo/dpi_socket_dir.
* \Return
* \li Socket directory name on success
* \li NULL on failure.
*/
char *init_sockdir(char *dpi_socket_dir)
{
char *sockdir = NULL;
int dir_ok = 0;
if ((sockdir = a_Dpi_rd_dpi_socket_dir(dpi_socket_dir)) == NULL) {
MSG_ERR("init_sockdir: The dpi_socket_dir file %s does not exist\n",
dpi_socket_dir);
} else {
if ((dir_ok = tst_dir(sockdir)) == 1) {
MSG_ERR("init_sockdir: The socket directory %s exists and is OK\n",
sockdir);
} else {
MSG_ERR("init_sockdir: The socket directory %s does not exist "
"or is not a directory\n", sockdir);
dFree(sockdir);
}
}
if (!dir_ok) {
sockdir = mk_sockdir();
if (sockdir == NULL) {
ERRMSG("init_sockdir", "mk_sockdir", 0);
MSG_ERR(" - Failed to create dpi socket directory\n");
} else if ((w_dpi_socket_dir(dpi_socket_dir, sockdir)) == -1) {
ERRMSG("init_sockdir", "w_dpi_socket_dir", 0);
MSG_ERR(" - failed to save %s\n", sockdir);
dFree(sockdir);
sockdir = NULL;
}
}
return (sockdir);
}

17
dpid/dpi_socket_dir.h Normal file
View File

@ -0,0 +1,17 @@
/*! \file
* Create a per user temporary directory for dpi sockets
*/
#ifndef DPI_SOCKET_DIR_H
#define DPI_SOCKET_DIR_H
int w_dpi_socket_dir(char *dirname, char *sockdir);
int tst_dir(char *dir);
char *mk_sockdir(void);
char *init_sockdir(char *dpi_socket_dir);
#endif

908
dpid/dpid.c Normal file
View File

@ -0,0 +1,908 @@
/*
Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
Copyright (C) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
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 3 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, see <http://www.gnu.org/licenses/>.
*/
/*! \file
* Main functions to set-up dpi information and to initialise sockets
*/
#include <errno.h>
#include <stdlib.h> /* for exit */
#include <fcntl.h> /* for F_SETFD, F_GETFD, FD_CLOEXEC */
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "dpid_common.h"
#include "dpid.h"
#include "dpi.h"
#include "dpi_socket_dir.h"
#include "misc_new.h"
#include "../dpip/dpip.h"
#define DPI_EXT (".dpi" EXEEXT)
#define QUEUE 5
volatile sig_atomic_t caught_sigchld = 0;
char *SharedKey = NULL;
/*! Remove dpid_comm_keys file.
* This avoids that dillo instances connect to a stale port after dpid
* has exited (e.g. after a reboot).
*/
void cleanup(void)
{
char *fname;
fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
unlink(fname);
dFree(fname);
}
/*! Free memory used to describe
* a set of dpi attributes
*/
void free_dpi_attr(struct dp *dpi_attr)
{
if (dpi_attr->id != NULL) {
dFree(dpi_attr->id);
dpi_attr->id = NULL;
}
if (dpi_attr->path != NULL) {
dFree(dpi_attr->path);
dpi_attr->path = NULL;
}
}
/*! Free memory used by the plugin list
*/
void free_plugin_list(struct dp **dpi_attr_list_ptr, int numdpis)
{
int i;
struct dp *dpi_attr_list = *dpi_attr_list_ptr;
if (dpi_attr_list == NULL)
return;
for (i = 0; i < numdpis; i++)
free_dpi_attr(dpi_attr_list + i);
dFree(dpi_attr_list);
*dpi_attr_list_ptr = NULL;
}
/*! Free memory used by the services list
*/
void free_services_list(Dlist *s_list)
{
int i = 0;
struct service *s;
for (i=0; i < dList_length(s_list) ; i++) {
s = dList_nth_data(s_list, i);
dFree(s->name);
}
dList_free(s_list);
}
/*! Signal handler for SIGINT, SIGQUIT, and SIGTERM. Calls cleanup
*/
static void terminator(int sig)
{
(void) sig; /* suppress unused parameter warning */
cleanup();
_exit(0);
}
/*! Establish handler for termination signals
* and register cleanup with atexit */
void est_dpi_terminator(void)
{
struct sigaction act;
sigset_t block;
sigemptyset(&block);
sigaddset(&block, SIGHUP);
sigaddset(&block, SIGINT);
sigaddset(&block, SIGQUIT);
sigaddset(&block, SIGTERM);
act.sa_handler = terminator;
act.sa_mask = block;
act.sa_flags = 0;
if (sigaction(SIGHUP, &act, NULL) ||
sigaction(SIGINT, &act, NULL) ||
sigaction(SIGQUIT, &act, NULL) ||
sigaction(SIGTERM, &act, NULL)) {
ERRMSG("est_dpi_terminator", "sigaction", errno);
exit(1);
}
if (atexit(cleanup) != 0) {
ERRMSG("est_dpi_terminator", "atexit", 0);
MSG_ERR("Hey! atexit failed, how did that happen?\n");
exit(1);
}
}
static int ends_with(const char *str, const char *suffix)
{
if (!str || !suffix)
return 0;
size_t lenstr = strlen(str);
size_t lensuffix = strlen(suffix);
if (lensuffix > lenstr)
return 0;
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}
/*! Identify a given file
* Currently there is only one file type associated with dpis.
* More file types will be added as needed
*/
enum file_type get_file_type(char *file_name)
{
if (ends_with(file_name, DPI_EXT))
/* Any filename ending in ".dpi" (or ".dpi.exe" on Windows) */
return DPI_FILE;
else {
MSG_ERR("get_file_type: Unknown file type for %s\n", file_name);
return UNKNOWN_FILE;
}
}
/*! Get dpi directory path from dpidrc
* \Return
* dpi directory on success, NULL on failure
* \Important
* The dpi_dir definition in dpidrc must have no leading white space.
*/
char *get_dpi_dir(char *dpidrc)
{
FILE *In;
int len;
char *rcline = NULL, *value = NULL, *p;
if ((In = fopen(dpidrc, "r")) == NULL) {
ERRMSG("dpi_dir", "fopen", errno);
MSG_ERR(" - %s\n", dpidrc);
return (NULL);
}
while ((rcline = dGetline(In)) != NULL) {
if (strncmp(rcline, "dpi_dir", 7) == 0)
break;
dFree(rcline);
}
fclose(In);
if (!rcline) {
ERRMSG("dpi_dir", "Failed to find a dpi_dir entry in dpidrc", 0);
MSG_ERR("Put your dillo plugins path in %s\n", dpidrc);
MSG_ERR("e.g. dpi_dir=/usr/local/lib/dillo/dpi\n");
MSG_ERR("with no leading spaces.\n");
value = NULL;
} else {
len = (int) strlen(rcline);
if (len && rcline[len - 1] == '\n')
rcline[len - 1] = 0;
if ((p = strchr(rcline, '='))) {
while (*++p == ' ');
value = dStrdup(p);
} else {
ERRMSG("dpi_dir", "strchr", 0);
MSG_ERR(" - '=' not found in %s\n", rcline);
value = NULL;
}
}
dFree(rcline);
return (value);
}
/*! Scans a service directory in dpi_dir and fills dpi_attr
* \Note
* Caller must allocate memory for dpi_attr.
* \Return
* \li 0 on success
* \li -1 on failure
* \todo
* Add other file types, but first we need to add files associated with a dpi
* to the design.
*/
int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr)
{
char *service_dir = NULL;
struct stat statinfo;
enum file_type ftype;
int ret = -1;
DIR *dir_stream;
struct dirent *dir_entry = NULL;
service_dir = dStrconcat(dpi_dir, "/", service, NULL);
if (stat(service_dir, &statinfo) == -1) {
ERRMSG("get_dpi_attr", "stat", errno);
MSG_ERR("file=%s\n", service_dir);
} else if ((dir_stream = opendir(service_dir)) == NULL) {
ERRMSG("get_dpi_attr", "opendir", errno);
} else {
/* Scan the directory looking for dpi files.
* (currently there's only the dpi program, but in the future
* there may also be helper scripts.) */
while ( (dir_entry = readdir(dir_stream)) != NULL) {
if (dir_entry->d_name[0] == '.')
continue;
ftype = get_file_type(dir_entry->d_name);
switch (ftype) {
case DPI_FILE:
dpi_attr->path =
dStrconcat(service_dir, "/", dir_entry->d_name, NULL);
dpi_attr->id = dStrdup(service);
dpi_attr->port = 0;
dpi_attr->pid = 1;
if (strstr(dpi_attr->path, ".filter") != NULL)
dpi_attr->filter = 1;
else
dpi_attr->filter = 0;
ret = 0;
break;
default:
break;
}
}
closedir(dir_stream);
if (ret != 0)
MSG_ERR("get_dpi_attr: No dpi plug-in in %s/%s\n",
dpi_dir, service);
}
dFree(service_dir);
return ret;
}
/*! Register a service
* Retrieves attributes for "service" and stores them
* in dpi_attr. It looks for "service" in ~/.dillo/dpi
* first, and then in the system wide dpi directory.
* Caller must allocate memory for dpi_attr.
* \Return
* \li 0 on success
* \li -1 on failure
*/
int register_service(struct dp *dpi_attr, char *service)
{
char *user_dpi_dir, *dpidrc, *user_service_dir, *dir = NULL;
int ret = -1;
user_dpi_dir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
user_service_dir =
dStrconcat(dGethomedir(), "/", dotDILLO_DPI, "/", service, NULL);
dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
if (access(dpidrc, F_OK) == -1) {
if (access(DPIDRC_SYS, F_OK) == -1) {
ERRMSG("register_service", "Error ", 0);
MSG_ERR("\n - There is no %s or %s file\n", dpidrc,
DPIDRC_SYS);
dFree(user_dpi_dir);
dFree(user_service_dir);
dFree(dpidrc);
return(-1);
}
dFree(dpidrc);
dpidrc = dStrdup(DPIDRC_SYS);
}
/* Check home dir for dpis */
if (access(user_service_dir, F_OK) == 0) {
get_dpi_attr(user_dpi_dir, service, dpi_attr);
ret = 0;
} else { /* Check system wide dpis */
if ((dir = get_dpi_dir(dpidrc)) != NULL) {
if (access(dir, F_OK) == 0) {
get_dpi_attr(dir, service, dpi_attr);
ret = 0;
} else {
ERRMSG("register_service", "get_dpi_attr failed", 0);
}
} else {
ERRMSG("register_service", "dpi_dir: Error getting dpi dir.", 0);
}
}
dFree(user_dpi_dir);
dFree(user_service_dir);
dFree(dpidrc);
dFree(dir);
return ret;
}
/*!
* Create dpi directory for available
* plugins and create plugin list.
* \Return
* \li Returns number of available plugins on success
* \li -1 on failure
*/
int register_all(struct dp **attlist)
{
char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
struct dirent *user_dirent, *sys_dirent;
int st;
int snum;
size_t dp_sz = sizeof(struct dp);
if (*attlist != NULL) {
ERRMSG("register_all", "attlist parameter should be NULL", 0);
return -1;
}
user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
if (access(user_dpidir, F_OK) == -1) {
/* no dpis in user's space */
dFree(user_dpidir);
user_dpidir = NULL;
}
dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
if (access(dpidrc, F_OK) == -1) {
dFree(dpidrc);
dpidrc = dStrdup(DPIDRC_SYS);
if (access(dpidrc, F_OK) == -1) {
dFree(dpidrc);
dpidrc = NULL;
}
}
if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
sys_dpidir = NULL;
dFree(dpidrc);
if (!user_dpidir && !sys_dpidir) {
ERRMSG("register_all", "Fatal error ", 0);
MSG_ERR("\n - Can't find the directory for dpis.\n");
exit(1);
}
/* Get list of services in user's .dillo/dpi directory */
snum = 0;
if (user_dpidir) {
DIR *user_dir_stream = opendir(user_dpidir);
/* Only complain if error is other than not found as the user may not have
* any DPIs installed. */
if (user_dir_stream == NULL && errno != ENOENT) {
MSG_ERR("cannot open user dpi directory '%s': %s\n",
user_dpidir, strerror(errno));
} else {
while ((user_dirent = readdir(user_dir_stream)) != NULL) {
if (user_dirent->d_name[0] == '.')
continue;
*attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz);
st=get_dpi_attr(user_dpidir, user_dirent->d_name, &(*attlist)[snum]);
if (st == 0)
snum++;
}
closedir(user_dir_stream);
}
}
if (sys_dpidir) {
DIR *sys_dir_stream = opendir(sys_dpidir);
/* For system DPIs always complain. */
if (sys_dir_stream == NULL) {
MSG_ERR("cannot open system dpi directory '%s': %s\n",
sys_dpidir, strerror(errno));
} else {
/* if system service is not in user list then add it */
while ((sys_dirent = readdir(sys_dir_stream)) != NULL) {
if (sys_dirent->d_name[0] == '.')
continue;
*attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz);
st=get_dpi_attr(sys_dpidir, sys_dirent->d_name, &(*attlist)[snum]);
if (st == 0)
snum++;
}
closedir(sys_dir_stream);
}
}
dFree(sys_dpidir);
dFree(user_dpidir);
/* TODO: do we consider snum == 0 an error?
* (if so, we should return -1 ) */
return (snum);
}
/*
* Compare two struct service pointers
* This function is used for sorting services
*/
static int services_alpha_comp(const struct service *s1,
const struct service *s2)
{
return -strcmp(s1->name, s2->name);
}
/*! Add services reading a dpidrc file
* each non empty or commented line has the form
* service = path_relative_to_dpidir
* \Return:
* \li Returns number of available services on success
* \li -1 on failure
*/
int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list)
{
FILE *dpidrc_stream;
char *p, *line = NULL, *service, *path;
int i, st;
struct service *s;
char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
if (access(user_dpidir, F_OK) == -1) {
/* no dpis in user's space */
dFree(user_dpidir);
user_dpidir = NULL;
}
dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
if (access(dpidrc, F_OK) == -1) {
dFree(dpidrc);
dpidrc = dStrdup(DPIDRC_SYS);
if (access(dpidrc, F_OK) == -1) {
dFree(dpidrc);
dpidrc = NULL;
}
}
if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
sys_dpidir = NULL;
if (!user_dpidir && !sys_dpidir) {
ERRMSG("fill_services_list", "Fatal error ", 0);
MSG_ERR("\n - Can't find the directory for dpis.\n");
exit(1);
}
if ((dpidrc_stream = fopen(dpidrc, "r")) == NULL) {
ERRMSG("fill_services_list", "popen failed", errno);
dFree(dpidrc);
dFree(sys_dpidir);
dFree(user_dpidir);
return (-1);
}
if (*services_list != NULL) {
ERRMSG("fill_services_list", "services_list parameter is not NULL", 0);
fclose(dpidrc_stream);
return -1;
}
*services_list = dList_new(8);
/* dpidrc parser loop */
for (;(line = dGetline(dpidrc_stream)) != NULL; dFree(line)) {
st = dParser_parse_rc_line(&line, &service, &path);
if (st < 0) {
MSG_ERR("dpid: Syntax error in %s: service=\"%s\" path=\"%s\"\n",
dpidrc, service, path);
continue;
} else if (st != 0) {
continue;
}
_MSG("dpid: service=%s, path=%s\n", service, path);
/* ignore dpi_dir silently */
if (strcmp(service, "dpi_dir") == 0)
continue;
s = dNew(struct service, 1);
/* init services list entry */
s->name = dStrdup(service);
s->dp_index = -1;
dList_append(*services_list, s);
/* search the dpi for a service by its path */
for (i = 0; i < numdpis; i++)
if ((p = strstr(attlist[i].path, path)) && *(p - 1) == '/' &&
strlen(p) == strlen(path))
break;
/* if the dpi exist bind service and dpi */
if (i < numdpis)
s->dp_index = i;
}
fclose(dpidrc_stream);
dList_sort(*services_list, (dCompareFunc)services_alpha_comp);
dFree(dpidrc);
dFree(sys_dpidir);
dFree(user_dpidir);
return (dList_length(*services_list));
}
/*
* Return a socket file descriptor
* (useful to set socket options in a uniform way)
*/
static int make_socket_fd(void)
{
int ret, one = 1;
if ((ret = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
ERRMSG("make_socket_fd", "socket", errno);
} else {
/* avoid delays when sending small pieces of data */
setsockopt(ret, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
}
/* set some buffering to increase the transfer's speed */
//setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF,
// &sock_buflen, (socklen_t)sizeof(sock_buflen));
return ret;
}
/*! Bind a socket port on localhost. Try to be close to base_port.
* \Return
* \li listening socket file descriptor on success
* \li -1 on failure
*/
int bind_socket_fd(int base_port, int *p_port)
{
int sock_fd, port;
struct sockaddr_in sin;
int ok = 0, last_port = base_port + 50;
if ((sock_fd = make_socket_fd()) == -1) {
return (-1); /* avoids nested ifs */
}
/* Set the socket FD to close on exec */
fcntl(sock_fd, F_SETFD, FD_CLOEXEC | fcntl(sock_fd, F_GETFD));
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
/* Try to bind a port on localhost */
for (port = base_port; port <= last_port; ++port) {
sin.sin_port = htons(port);
if ((bind(sock_fd, (struct sockaddr *)&sin, sizeof(sin))) == -1) {
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL)
continue;
ERRMSG("bind_socket_fd", "bind", errno);
} else if (listen(sock_fd, QUEUE) == -1) {
ERRMSG("bind_socket_fd", "listen", errno);
} else {
*p_port = port;
ok = 1;
break;
}
}
if (port > last_port) {
MSG_ERR("Hey! Can't find an available port from %d to %d\n",
base_port, last_port);
}
return ok ? sock_fd : -1;
}
/*! Save the current port and a shared secret in a file so dillo can find it.
* \Return:
* \li -1 on failure
*/
int save_comm_keys(int srs_port)
{
int fd, ret = -1;
char *fname, port_str[32];
fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
dFree(fname);
if (fd == -1) {
MSG("save_comm_keys: open %s\n", dStrerror(errno));
} else {
snprintf(port_str, 16, "%d %s\n", srs_port, SharedKey);
if (CKD_WRITE(fd, port_str) != -1 && CKD_CLOSE(fd) != -1) {
ret = 1;
}
}
return ret;
}
/*! Initialise the service request socket (IDS)
* \Return:
* \li Number of sockets (1 == success)
* \li -1 on failure
*/
int init_ids_srs_socket(void)
{
int srs_port, ret = -1;
FD_ZERO(&sock_set);
if ((srs_fd = bind_socket_fd(DPID_BASE_PORT, &srs_port)) != -1) {
/* create the shared secret */
SharedKey = a_Misc_mksecret(8);
/* save port number and SharedKey */
if (save_comm_keys(srs_port) != -1) {
FD_SET(srs_fd, &sock_set);
ret = 1;
}
}
return ret;
}
/*! Initialize a single dpi socket
* \Return
* \li 1 on success
* \li -1 on failure
*/
int init_dpi_socket(struct dp *dpi_attr)
{
int s_fd, port, ret = -1;
if ((s_fd = bind_socket_fd(DPID_BASE_PORT, &port)) != -1) {
dpi_attr->sock_fd = s_fd;
dpi_attr->port = port;
FD_SET(s_fd, &sock_set);
ret = 1;
}
return ret;
}
/*! Setup sockets for the plugins and add them to
* the set of sockets (sock_set) watched by select.
* \Return
* \li Number of sockets on success
* \li -1 on failure
* \Modifies
* dpi_attr_list.sa, dpi_attr_list.socket, numsocks, sock_set, srs
* \Uses
* numdpis, srs, srs_name
*/
int init_all_dpi_sockets(struct dp *dpi_attr_list)
{
int i;
/* Initialise sockets for each dpi */
for (i = 0; i < numdpis; i++) {
if (init_dpi_socket(dpi_attr_list + i) == -1)
return (-1);
numsocks++;
}
return (numsocks);
}
/*! SIGCHLD handler
*/
void dpi_sigchld(int sig)
{
if (sig == SIGCHLD)
caught_sigchld = 1;
}
/*! Called by main loop when caught_sigchld == 1 */
void handle_sigchld(void)
{
// pid_t pid;
int i, status; //, num_active;
/* For all of the dpis in the current list
* add the ones that have exited to the set of sockets being
* watched by 'select'.
*/
for (i = 0; i < numdpis; i++) {
if (waitpid(dpi_attr_list[i].pid, &status, WNOHANG) > 0) {
dpi_attr_list[i].pid = 1;
FD_SET(dpi_attr_list[i].sock_fd, &sock_set);
numsocks++;
}
}
/* Wait for any old dpis that have exited */
while (waitpid(-1, &status, WNOHANG) > 0)
;
}
/*! Establish SIGCHLD handler */
void est_dpi_sigchld(void)
{
struct sigaction sigact;
sigset_t set;
(void) sigemptyset(&set);
sigact.sa_handler = dpi_sigchld;
sigact.sa_mask = set;
sigact.sa_flags = SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sigact, NULL) == -1) {
ERRMSG("est_dpi_sigchld", "sigaction", errno);
exit(1);
}
}
/*! EINTR aware connect() call */
int ckd_connect (int sock_fd, struct sockaddr *addr, socklen_t len)
{
ssize_t ret;
do {
ret = connect(sock_fd, addr, len);
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
ERRMSG("dpid.c", "connect", errno);
}
return ret;
}
/*! Send DpiBye command to all active non-filter dpis
*/
void stop_active_dpis(struct dp *dpi_attr_list, int numdpis)
{
char *bye_cmd, *auth_cmd;
int i, sock_fd;
struct sockaddr_in sin;
bye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
auth_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
for (i = 0; i < numdpis; i++) {
/* Skip inactive dpis and filters */
if (dpi_attr_list[i].pid == 1 || dpi_attr_list[i].filter)
continue;
if ((sock_fd = make_socket_fd()) == -1) {
ERRMSG("stop_active_dpis", "socket", errno);
continue;
}
sin.sin_port = htons(dpi_attr_list[i].port);
if (ckd_connect(sock_fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
ERRMSG("stop_active_dpis", "connect", errno);
MSG_ERR("%s\n", dpi_attr_list[i].path);
} else if (CKD_WRITE(sock_fd, auth_cmd) == -1) {
ERRMSG("stop_active_dpis", "write", errno);
} else if (CKD_WRITE(sock_fd, bye_cmd) == -1) {
ERRMSG("stop_active_dpis", "write", errno);
}
dClose(sock_fd);
}
dFree(auth_cmd);
dFree(bye_cmd);
/* Allow child dpis some time to read dpid_comm_keys before erasing it */
sleep (1);
}
/*! Removes dpis in dpi_attr_list from the
* set of sockets watched by select and
* closes their sockets.
*/
void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
{
int i;
for (i = 0; i < numdpis; i++) {
FD_CLR(dpi_attr_list[i].sock_fd, &sock_set);
dClose(dpi_attr_list[i].sock_fd);
}
}
/*! Registers available dpis and stops active non-filter dpis.
* Called when dpid receives
* cmd='register' service='all'
* command
* \Return
* Number of available dpis
*/
int register_all_cmd(void)
{
stop_active_dpis(dpi_attr_list, numdpis);
free_plugin_list(&dpi_attr_list, numdpis);
free_services_list(services_list);
services_list = NULL;
numdpis = 0;
numsocks = 1; /* the srs socket */
FD_ZERO(&sock_set);
FD_SET(srs_fd, &sock_set);
numdpis = register_all(&dpi_attr_list);
fill_services_list(dpi_attr_list, numdpis, &services_list);
numsocks = init_all_dpi_sockets(dpi_attr_list);
return (numdpis);
}
/*!
* Get value of msg field from dpi_tag
* \Return
* message on success, NULL on failure
*/
char *get_message(int sock_fd, char *dpi_tag)
{
char *msg, *d_cmd;
msg = a_Dpip_get_attr(dpi_tag, "msg");
if (msg == NULL) {
ERRMSG("get_message", "failed to parse msg", 0);
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
"DpiError", "Failed to parse request");
(void) CKD_WRITE(sock_fd, d_cmd);
dFree(d_cmd);
}
return (msg);
}
/*
* Compare a struct service pointer and a service name
* This function is used for searching services by name
*/
int service_match(const struct service *A, const char *B)
{
int A_len, B_len, len;
A_len = strlen(A->name);
B_len = strlen(B);
len = MAX (A_len, B_len);
if (A->name[A_len - 1] == '*')
len = A_len - 1;
return(dStrnAsciiCasecmp(A->name, B, len));
}
/*!
* Send socket port that matches dpi_id to client
*/
void send_sockport(int sock_fd, char *dpi_tag, struct dp *dpi_attr_list)
{
int i;
char *dpi_id, *d_cmd, port_str[16];
struct service *serv;
dReturn_if_fail((dpi_id = get_message(sock_fd, dpi_tag)) != NULL);
serv = dList_find_custom(services_list,dpi_id,(dCompareFunc)service_match);
if (serv == NULL || (i = serv->dp_index) == -1)
for (i = 0; i < numdpis; i++)
if (!strncmp(dpi_attr_list[i].id, dpi_id,
dpi_attr_list[i].id - strchr(dpi_attr_list[i].id, '.')))
break;
if (i < numdpis) {
/* found */
snprintf(port_str, 8, "%d", dpi_attr_list[i].port);
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_data", port_str);
(void) CKD_WRITE(sock_fd, d_cmd);
dFree(d_cmd);
}
dFree(dpi_id);
}

115
dpid/dpid.h Normal file
View File

@ -0,0 +1,115 @@
/*! \file
* Main functions to set-up dpi information and to initialise sockets
*/
#ifndef DPID_H
#define DPID_H
#include <sys/socket.h>
#include <sys/select.h> /* for fd_set */
#include <sys/un.h>
#include <signal.h> /* for sig_atomic_t */
#include <netinet/in.h> /* for ntohl, IPPORT_USERRESERVED and stuff */
#include "d_size.h"
/* FreeBSD 6.4 doesn't have it */
#ifndef IPPORT_USERRESERVED
#define IPPORT_USERRESERVED 5000
#endif
#define PATH_LEN 50
#define CMDLEN 20
#define MSGLEN 50
#define DPID_BASE_PORT (IPPORT_USERRESERVED + 20)
/*! \TODO: Should read this from dillorc */
#define SRS_NAME "dpid.srs"
extern char *srs_name;
/*! dpid's service request socket file descriptor */
extern int srs_fd;
/*! plugin state information
*/
struct dp {
char *id;
char *path;
int sock_fd;
int port;
pid_t pid;
int filter;
};
/*! bind dpi with service
*/
struct service {
char *name;
int dp_index;
};
/*! Number of available plugins */
extern int numdpis;
/*! Number of sockets being watched */
extern int numsocks;
/*! State information for each plugin. */
extern struct dp *dpi_attr_list;
/*! service served for each plugin */
extern Dlist *services_list;
/*! Set of sockets watched for connections */
extern fd_set sock_set;
/*! Set to 1 by the SIGCHLD handler dpi_sigchld */
extern volatile sig_atomic_t caught_sigchld;
void rm_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
void cleanup(void);
void free_dpi_attr(struct dp *dpi_attr);
void free_plugin_list(struct dp **dpi_attr_list_ptr, int numdpis);
void free_services_list(Dlist *s_list);
enum file_type get_file_type(char *file_name);
int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr);
int register_service(struct dp *dpi_attr, char *service);
int register_all(struct dp **attlist);
int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list);
int init_ids_srs_socket(void);
int init_dpi_socket(struct dp *dpi_attr);
int init_all_dpi_sockets(struct dp *dpi_attr_list);
void dpi_sigchld(int sig);
void handle_sigchld(void);
void est_dpi_sigchld(void);
void est_dpi_terminator(void);
void stop_active_dpis(struct dp *dpi_attr_list, int numdpis);
void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
int register_all_cmd(void);
char *get_message(int sock, char *dpi_tag);
int service_match(const struct service *A, const char *B);
void send_sockport(int sock_fd, char * dpi_tag, struct dp *dpi_attr_list);
#endif

59
dpid/dpid_common.c Normal file
View File

@ -0,0 +1,59 @@
/*
* File: dpid_common.c
*
* Copyright 2008 Jorge Arellano Cid <jcid@dillo.org>
*
* 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 3 of the License, or
* (at your option) any later version.
*/
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include "dpid_common.h"
/*
* Send a verbose error message.
*/
void errmsg(char *caller, char *called, int errornum, char *file, int line)
{
MSG_ERR("%s:%d: %s: %s\n", file, line, caller, called);
if (errornum > 0)
MSG_ERR("%s\n", dStrerror(errornum));
}
/*!
* Provides an error checked write command.
* Call this via the CKD_WRITE macro
* \return write return value
*/
ssize_t ckd_write(int fd, char *msg, char *file, int line)
{
ssize_t ret;
do {
ret = write(fd, msg, strlen(msg));
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
MSG_ERR("%s:%d: write: %s\n", file, line, dStrerror(errno));
}
return (ret);
}
/*!
* Provides an error checked close() call.
* Call this via the CKD_CLOSE macro
* \return close return value
*/
ssize_t ckd_close(int fd, char *file, int line)
{
ssize_t ret;
ret = dClose(fd);
if (ret == -1)
MSG_ERR("%s:%d: close: %s\n", file, line, dStrerror(errno));
return ret;
}

54
dpid/dpid_common.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef DPID_COMMON_H
#define DPID_COMMON_H
/*! \file
* Declares common functions, global variables, and types.
*
* \todo
* The dpid error codes will be used in
* the next patch
*/
#include <dirent.h>
#include <stddef.h> /* size_t */
#include <sys/types.h> /* ssize_t */
#include "../dlib/dlib.h"
/*
* Debugging macros
*/
#define _MSG(...)
#define MSG(...) printf("[dpid]: " __VA_ARGS__)
#define MSG_ERR(...) fprintf(stderr, "[dpid]: " __VA_ARGS__)
#define dotDILLO_DPI ".dillo/dpi"
#define dotDILLO_DPIDRC ".dillo/dpidrc"
#define dotDILLO_DPID_COMM_KEYS ".dillo/dpid_comm_keys"
#define ERRMSG(CALLER, CALLED, ERR)\
errmsg(CALLER, CALLED, ERR, __FILE__, __LINE__)
#define _ERRMSG(CALLER, CALLED, ERR)
/*!
* Macros for calling ckd_write and ckd_close functions
*/
#define CKD_WRITE(fd, msg) ckd_write(fd, msg, __FILE__, __LINE__)
#define CKD_CLOSE(fd) ckd_close(fd, __FILE__, __LINE__)
/*! Intended for identifying dillo plugins
* and related files
*/
enum file_type {
DPI_FILE, /*! Any file name containing .dpi */
UNKNOWN_FILE
};
void errmsg(char *caller, char *called, int errornum, char *file, int line);
ssize_t ckd_write(int fd, char *msg, char *file, int line);
ssize_t ckd_close(int fd, char *file, int line);
#endif

133
dpid/dpidc.c Normal file
View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2009-2013 Jorge Arellano Cid <jcid@dillo.org>
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* 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 3 of the License, or
* (at your option) any later version.
*/
#include <stdio.h>
#include <stdlib.h> /* for exit */
#include <string.h> /* for memset */
#include <unistd.h> /* for read and write */
#include <ctype.h> /* for isxdigit */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include "../dlib/dlib.h"
#include "../dpip/dpip.h"
#define MSG_ERR(...) printf("** ERROR **: " __VA_ARGS__);
char *CMD_REGISTER = "<cmd='register_all' '>";
char *CMD_STOP = "<cmd='DpiBye' '>";
static char SharedKey[32];
static void print_usage(const char *prgname)
{
fprintf(stderr,"Control program for the Dillo plugin daemon\n"
"Usage: %s {stop|register|chat}\n\n", prgname);
}
static void error(char *msg)
{
perror(msg);
exit(1);
}
/**
* Read dpid's communication keys from its saved file.
* Return value: 1 on success, -1 on error.
*/
static int Dpi_read_comm_keys(int *port)
{
FILE *In;
char *fname, *rcline = NULL, *tail;
int i, ret = -1;
fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
if ((In = fopen(fname, "r")) == NULL) {
MSG_ERR("[Dpi_read_comm_keys] %s\n", dStrerror(errno));
} else if ((rcline = dGetline(In)) == NULL) {
MSG_ERR("[Dpi_read_comm_keys] empty file: %s\n", fname);
} else {
*port = strtol(rcline, &tail, 10);
for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
SharedKey[i] = tail[i+1];
SharedKey[i] = 0;
ret = 1;
}
dFree(rcline);
dFree(fname);
return ret;
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
char buffer[256];
if (argc != 2) {
print_usage(argv[0]);
exit(1);
}
/* Read dpid's port number from saved file */
if (Dpi_read_comm_keys(&portno) == -1) {
MSG_ERR("main: Can't read dpid's port number\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(portno);
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
error("ERROR connecting");
snprintf(buffer, sizeof(buffer), "<cmd='auth' msg='%s' '>", SharedKey);
n = write(sockfd, buffer, strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
if (strcmp(argv[1], "stop") == 0) {
strcpy(buffer, CMD_STOP);
} else if (strcmp(argv[1], "register") == 0) {
strcpy(buffer, CMD_REGISTER);
} else if (strcmp(argv[1], "chat") == 0) {
printf("Please enter the message: ");
memset(buffer,0,256);
if (fgets(buffer,255,stdin) == NULL)
MSG_ERR("dpidc: Can't read the message\n");
} else {
MSG_ERR("main: Unknown operation '%s'\n", argv[1]);
print_usage(argv[0]);
exit(1);
}
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
/*
memset(buffer,0,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
*/
dClose(sockfd);
return 0;
}

5
dpid/dpidrc.in Normal file
View File

@ -0,0 +1,5 @@
dpi_dir=@libdir@/dillo/dpi
proto.file=file/file.dpi@EXEEXT@
proto.ftp=ftp/ftp.filter.dpi@EXEEXT@
proto.data=datauri/datauri.filter.dpi@EXEEXT@

422
dpid/main.c Normal file
View File

@ -0,0 +1,422 @@
/*
Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
Copyright (C) 2020 Axel Beckert <abe@debian.org>
Copyright (C) 2023 Michal Grezl <walley@walley.org>
Copyright (C) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h> /* for ckd_write */
#include <unistd.h> /* for ckd_write */
#include <stdlib.h> /* for exit */
#include <assert.h> /* for assert */
#include <sys/stat.h> /* for umask */
#include "dpid_common.h"
#include "dpid.h"
#include "dpi.h"
#include "dpi_socket_dir.h"
#include "misc_new.h"
#include "../dlib/dlib.h"
#include "../dpip/dpip.h"
sigset_t mask_sigchld;
/* fix for gcc 10 */
enum {
no_errors,
dpid_srs_addrinuse /* dpid service request socket address already in use */
} dpi_errno;
char *srs_name;
int numdpis;
fd_set sock_set;
struct dp *dpi_attr_list;
Dlist *services_list;
int numsocks;
int srs_fd;
// end of fix
/** Start a dpi filter plugin after accepting the pending connection
* \Return
* \li Child process ID on success
* \li 0 on failure
*/
static int start_filter_plugin(struct dp dpi_attr)
{
int newsock, old_stdout=-1, old_stdin=-1;
socklen_t csz;
struct sockaddr_un clnt_addr;
pid_t pid;
csz = (socklen_t) sizeof(clnt_addr);
newsock = accept(dpi_attr.sock_fd, (struct sockaddr *) &clnt_addr, &csz);
if (newsock == -1)
ERRMSG("start_plugin", "accept", errno);
dup2(STDIN_FILENO, old_stdin);
if (dup2(newsock, STDIN_FILENO) == -1) {
ERRMSG("start_plugin", "dup2", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
}
dup2(STDOUT_FILENO, old_stdout);
if (dup2(newsock, STDOUT_FILENO) == -1) {
ERRMSG("start_plugin", "dup2", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
}
if ((pid = fork()) == -1) {
ERRMSG("main", "fork", errno);
return 0;
}
if (pid == 0) {
/* Child, start plugin */
if (execl(dpi_attr.path, dpi_attr.path, (char*)NULL) == -1) {
ERRMSG("start_plugin", "execl", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
}
}
/* Parent, Close sockets fix stdio and return pid */
if (dClose(newsock) == -1) {
ERRMSG("start_plugin", "close", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
}
dClose(STDIN_FILENO);
dClose(STDOUT_FILENO);
dup2(old_stdin, STDIN_FILENO);
dup2(old_stdout, STDOUT_FILENO);
return pid;
}
static void start_server_plugin(struct dp dpi_attr)
{
if (dup2(dpi_attr.sock_fd, STDIN_FILENO) == -1) {
ERRMSG("start_plugin", "dup2", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
}
if (dClose(dpi_attr.sock_fd) == -1) {
ERRMSG("start_plugin", "close", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
}
if (execl(dpi_attr.path, dpi_attr.path, (char*)NULL) == -1) {
ERRMSG("start_plugin", "execl", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
}
}
/*!
* Read service request from sock
* \Return
* pointer to dynamically allocated request tag
*/
static char *get_request(Dsh *sh)
{
char *dpip_tag;
(void) sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
(void) sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
return dpip_tag;
}
/*!
* Get value of cmd field in dpi_tag
* \Return
* command code on success, -1 on failure
*/
static int get_command(Dsh *sh, char *dpi_tag)
{
char *cmd, *d_cmd;
int COMMAND;
if (dpi_tag == NULL) {
_ERRMSG("get_command", "dpid tag is NULL", 0);
return (-1);
}
cmd = a_Dpip_get_attr(dpi_tag, "cmd");
if (cmd == NULL) {
ERRMSG("get_command", "a_Dpip_get_attr", 0);
MSG_ERR(": dpid failed to parse cmd in %s\n", dpi_tag);
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
"DpiError", "Failed to parse request");
a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
COMMAND = -1;
} else if (strcmp("auth", cmd) == 0) {
COMMAND = AUTH_CMD;
} else if (strcmp("DpiBye", cmd) == 0) {
COMMAND = BYE_CMD;
} else if (strcmp("check_server", cmd) == 0) {
COMMAND = CHECK_SERVER_CMD;
} else if (strcmp("register_all", cmd) == 0) {
COMMAND = REGISTER_ALL_CMD;
} else if (strcmp("register_service", cmd) == 0) {
COMMAND = REGISTER_SERVICE_CMD;
} else { /* Error unknown command */
COMMAND = UNKNOWN_CMD;
}
dFree(cmd);
return (COMMAND);
}
/**
* Check whether a dpi server is running
*/
static int server_is_running(char *server_id)
{
int i;
/* Search in the set of running servers */
for (i = 0; i < numdpis; i++) {
if (!dpi_attr_list[i].filter && dpi_attr_list[i].pid > 1 &&
strcmp(dpi_attr_list[i].id, server_id) == 0)
return 1;
}
return 0;
}
/**
* Get MAX open FD limit (yes, it's tricky --Jcid).
*/
static int get_open_max(void)
{
#ifdef OPEN_MAX
return OPEN_MAX;
#else
int ret = sysconf(_SC_OPEN_MAX);
if (ret < 0)
ret = 256;
return ret;
#endif
}
/*! \todo
* \li Add a dpid_idle_timeout variable to dpidrc
* \bug Infinite loop if plugin crashes before it accepts a connection
*/
int main(void)
{
int i, n = 0, open_max;
int dpid_idle_timeout = 60 * 60; /* default, in seconds */
struct timeval select_timeout;
sigset_t mask_none;
fd_set selected_set;
dpi_attr_list = NULL;
services_list = NULL;
//daemon(0,0); /* Use 0,1 for feedback */
/* TODO: call setsid() ?? */
/* Allow read and write access, but only for the user.
* TODO: can this cause trouble with umount? */
umask(0077);
/* TODO: make dpid work on any directory. */
// chdir("/");
/* close inherited file descriptors */
open_max = get_open_max();
for (i = 3; i < open_max; i++)
dClose(i);
/* this sleep used to unmask a race condition */
// sleep(2);
dpi_errno = no_errors;
/* Get list of available dpis */
numdpis = register_all(&dpi_attr_list);
#if 0
/* Get name of socket directory */
dirname = a_Dpi_sockdir_file();
if ((sockdir = init_sockdir(dirname)) == NULL) {
ERRMSG("main", "init_sockdir", 0);
MSG_ERR("Failed to create socket directory\n");
exit(1);
}
#endif
/* Init and get services list */
fill_services_list(dpi_attr_list, numdpis, &services_list);
/* Remove any sockets that may have been leftover from a crash */
//cleanup();
/* Initialise sockets */
if ((numsocks = init_ids_srs_socket()) == -1) {
switch (dpi_errno) {
case dpid_srs_addrinuse:
MSG_ERR("dpid refuses to start, possibly because:\n");
MSG_ERR("\t1) An instance of dpid is already running.\n");
MSG_ERR("\t2) A previous dpid didn't clean up on exit.\n");
exit(1);
default:
//ERRMSG("main", "init_srs_socket failed", 0);
ERRMSG("main", "init_ids_srs_socket failed", 0);
exit(1);
}
}
numsocks = init_all_dpi_sockets(dpi_attr_list);
est_dpi_terminator();
est_dpi_sigchld();
(void) sigemptyset(&mask_sigchld);
(void) sigaddset(&mask_sigchld, SIGCHLD);
(void) sigemptyset(&mask_none);
(void) sigprocmask(SIG_SETMASK, &mask_none, NULL);
printf("dpid started (found %d dpis)\n", numdpis);
/* Start main loop */
while (1) {
do {
(void) sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
if (caught_sigchld) {
handle_sigchld();
caught_sigchld = 0;
}
(void) sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
select_timeout.tv_sec = dpid_idle_timeout;
select_timeout.tv_usec = 0;
selected_set = sock_set;
n = select(FD_SETSIZE, &selected_set, NULL, NULL, &select_timeout);
if (n == 0) { /* select timed out, try to exit */
/* BUG: This is a workaround for dpid not to exit when the
* downloads server is active. The proper way to handle it is with
* a dpip command that asks the server whether it's busy.
* Note: the cookies server may lose session info too. */
if (server_is_running("downloads"))
continue;
stop_active_dpis(dpi_attr_list, numdpis);
//cleanup();
exit(0);
}
} while (n == -1 && errno == EINTR);
if (n == -1) {
ERRMSG("main", "select", errno);
exit(1);
}
/* If the service req socket is selected then service the req. */
if (FD_ISSET(srs_fd, &selected_set)) {
int sock_fd;
socklen_t sin_sz;
struct sockaddr_in sin;
char *req = NULL;
--n;
assert(n >= 0);
sin_sz = (socklen_t) sizeof(sin);
sock_fd = accept(srs_fd, (struct sockaddr *)&sin, &sin_sz);
if (sock_fd == -1) {
ERRMSG("main", "accept", errno);
MSG_ERR("accept on srs socket failed\n");
MSG_ERR("service pending connections, and continue\n");
} else {
int command;
Dsh *sh;
sh = a_Dpip_dsh_new(sock_fd, sock_fd, 1024);
read_next:
req = get_request(sh);
command = get_command(sh, req);
switch (command) {
case AUTH_CMD:
if (a_Dpip_check_auth(req) != -1) {
dFree(req);
goto read_next;
}
break;
case BYE_CMD:
stop_active_dpis(dpi_attr_list, numdpis);
//cleanup();
exit(0);
break;
case CHECK_SERVER_CMD:
send_sockport(sock_fd, req, dpi_attr_list);
break;
case REGISTER_ALL_CMD:
register_all_cmd();
break;
case UNKNOWN_CMD:
{
char *d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
"DpiError", "Unknown command");
(void) CKD_WRITE(sock_fd, d_cmd);
dFree(d_cmd);
ERRMSG("main", "Unknown command", 0);
MSG_ERR(" for request: %s\n", req);
break;
}
case -1:
_ERRMSG("main", "get_command failed", 0);
break;
}
if (req)
free(req);
a_Dpip_dsh_close(sh);
a_Dpip_dsh_free(sh);
}
}
/* While there's a request on one of the plugin sockets
* find the matching plugin and start it. */
for (i = 0; n > 0 && i < numdpis; i++) {
if (FD_ISSET(dpi_attr_list[i].sock_fd, &selected_set)) {
--n;
assert(n >= 0);
if (dpi_attr_list[i].filter) {
/* start a dpi filter plugin and continue watching its socket
* for new connections */
(void) sigprocmask(SIG_SETMASK, &mask_none, NULL);
start_filter_plugin(dpi_attr_list[i]);
} else {
/* start a dpi server plugin but don't wait for new connections
* on its socket */
numsocks--;
assert(numsocks >= 0);
FD_CLR(dpi_attr_list[i].sock_fd, &sock_set);
if ((dpi_attr_list[i].pid = fork()) == -1) {
ERRMSG("main", "fork", errno);
/* exit(1); */
} else if (dpi_attr_list[i].pid == 0) {
/* child */
(void) sigprocmask(SIG_SETMASK, &mask_none, NULL);
start_server_plugin(dpi_attr_list[i]);
}
}
}
}
}
}

199
dpid/misc_new.c Normal file
View File

@ -0,0 +1,199 @@
/*
* File: misc_new.c
*
* Copyright 2008 Jorge Arellano Cid <jcid@dillo.org>
*
* 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 3 of the License, or
* (at your option) any later version.
*/
#include <errno.h> /* errno, err-codes */
#include <unistd.h>
#include <time.h>
#include <sys/stat.h> /* stat */
#include <stdlib.h> /* rand, srand */
#include "../dlib/dlib.h"
#include "dpid_common.h"
#include "misc_new.h" /* for function prototypes */
/*! Reads a dpi tag from a socket
* \li Continues after a signal interrupt
* \Return
* Dstr pointer to tag on success, NULL on failure
* \important Caller is responsible for freeing the returned Dstr *
*/
Dstr *a_Misc_rdtag(int socket)
{
char c = '\0';
ssize_t rdlen;
Dstr *tag;
tag = dStr_sized_new(64);
errno = 0;
do {
rdlen = read(socket, &c, 1);
if (rdlen == -1 && errno != EINTR)
break;
dStr_append_c(tag, c);
} while (c != '>');
if (rdlen == -1) {
perror("a_Misc_rdtag");
dStr_free(tag, TRUE);
return (NULL);
}
return (tag);
}
/*!
* Read a dpi tag from sock
* \return
* pointer to dynamically allocated request tag
*/
char *a_Misc_readtag(int sock)
{
char *tag, c;
size_t i;
size_t tagmem = 10;
ssize_t rdln = 1;
tag = NULL;
// new start
tag = (char *) dMalloc(tagmem + 1);
for (i = 0; (rdln = read(sock, &c, 1)) != 0; i++) {
if (i == tagmem) {
tagmem += tagmem;
tag = (char *) dRealloc(tag, tagmem + 1);
}
tag[i] = c;
if (c == '>') {
tag[i + 1] = '\0';
break;
}
}
// new end
if (rdln == -1) {
ERRMSG("a_Misc_readtag", "read", errno);
}
return (tag);
}
/*! Reads a dpi tag from a socket without hanging on read.
* \li Continues after a signal interrupt
* \Return
* \li 1 on success
* \li 0 if input is not available within timeout microseconds.
* \li -1 on failure
* \important Caller is responsible for freeing the returned Dstr *
*/
/* Is this useful?
int a_Misc_nohang_rdtag(int socket, int timeout, Dstr **tag)
{
int n_fd;
fd_set sock_set, select_set;
struct timeval tout;
FD_ZERO(&sock_set);
FD_SET(socket, &sock_set);
errno = 0;
do {
select_set = sock_set;
tout.tv_sec = 0;
tout.tv_usec = timeout;
n_fd = select(socket + 1, &select_set, NULL, NULL, &tout);
} while (n_fd == -1 && errno == EINTR);
if (n_fd == -1) {
MSG_ERR("%s:%d: a_Misc_nohang_rdtag: %s\n",
__FILE__, __LINE__, dStrerror(errno));
return(-1);
}
if (n_fd == 0) {
return(0);
} else {
*tag = a_Misc_rdtag(socket);
return(1);
}
}
*/
/**
* Alternative to mkdtemp().
* Not as strong as mkdtemp, but enough for creating a directory.
*/
char *a_Misc_mkdtemp(char *template)
{
for (;;) {
if (a_Misc_mkfname(template) && mkdir(template, 0700) == 0)
break;
if (errno == EEXIST)
continue;
return 0;
}
return template;
}
/**
* Return a new, nonexistent file name from a template.
* (adapted from dietlibc; alternative to mkdtemp())
*/
char *a_Misc_mkfname(char *template)
{
char *tmp = template + strlen(template) - 6;
int i;
uint_t random;
struct stat stat_buf;
if (tmp < template)
goto error;
for (i = 0; i < 6; ++i)
if (tmp[i] != 'X') {
error:
errno = EINVAL;
return 0;
}
srand((uint_t)(time(0) ^ getpid()));
for (;;) {
random = (unsigned) rand();
for (i = 0; i < 6; ++i) {
int hexdigit = (random >> (i * 5)) & 0x1f;
tmp[i] = hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
}
if (stat(template, &stat_buf) == -1 && errno == ENOENT)
return template;
MSG_ERR("a_Misc_mkfname: another round for %s \n", template);
}
}
/**
* Return a new, random hexadecimal string of 'nchar' characters.
*/
char *a_Misc_mksecret(int nchar)
{
int i;
uint_t random;
char *secret = dNew(char, nchar + 1);
srand((uint_t)(time(0) ^ getpid()));
random = (unsigned) rand();
for (i = 0; i < nchar; ++i) {
int hexdigit = (random >> (i * 5)) & 0x0f;
secret[i] = hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
}
secret[i] = 0;
MSG("a_Misc_mksecret: %s\n", secret);
return secret;
}

10
dpid/misc_new.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef MISC_NEW_H
#define MISC_NEW_H
Dstr *a_Misc_rdtag(int socket);
char *a_Misc_readtag(int sock);
char *a_Misc_mkdtemp(char *template);
char *a_Misc_mkfname(char *template);
char *a_Misc_mksecret(int nchar);
#endif