diff mbox

[12/17] Add server.h and server.c

Message ID 1500926714-56988-13-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm July 24, 2017, 8:05 p.m. UTC
This patch adds a "server" abstract base class for listening
on a port, for use by the later patches to implement an LSP server.

It's largely adapted from examples in glibc's docs.  I suspect that
I've introduced platform-specific assumptions (and that it may need some
extra configure tests for the extra functionality), but this part of
the kit is just a proof-of-concept.

gcc/ChangeLog:
	* Makefile.in (OBJS): Add server.o.
	* server.c: New file.
	* server.h: New file.
---
 gcc/Makefile.in |   1 +
 gcc/server.c    | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gcc/server.h    |  46 +++++++++++++++++
 3 files changed, 199 insertions(+)
 create mode 100644 gcc/server.c
 create mode 100644 gcc/server.h

Comments

Oleg Endo July 26, 2017, 2:35 p.m. UTC | #1
On Mon, 2017-07-24 at 16:05 -0400, David Malcolm wrote:

> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GCC_SERVER_H
> +#define GCC_SERVER_H
> +
> +/* Wrapper aroung "int" for file descriptors.  */
                ~~~^
              around :)
David Malcolm July 26, 2017, 2:50 p.m. UTC | #2
On Wed, 2017-07-26 at 23:35 +0900, Oleg Endo wrote:
> On Mon, 2017-07-24 at 16:05 -0400, David Malcolm wrote:
> >  
> > +
> > +You should have received a copy of the GNU General Public License
> > +along with GCC; see the file COPYING3.  If not see
> > +<http://www.gnu.org/licenses/>.  */
> > +
> > +#ifndef GCC_SERVER_H
> > +#define GCC_SERVER_H
> > +
> > +/* Wrapper aroung "int" for file descriptors.  */
>                 ~~~^
>               around :)

Thanks; fixed in my working copy.

Someone pointed out to me privately that instead/as well as serving on
a port, we could be launched as a subprocess by the IDE, and serve the
RPC over stdin/stdout; this would be simpler for IDEs to cope with.  I
may have a look at supporting that for the next version.

Dave
Mike Stump July 26, 2017, 9 p.m. UTC | #3
On Jul 26, 2017, at 7:50 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> 
> On Wed, 2017-07-26 at 23:35 +0900, Oleg Endo wrote:
>> On Mon, 2017-07-24 at 16:05 -0400, David Malcolm wrote:
>>> 
>>> +
>>> +You should have received a copy of the GNU General Public License
>>> +along with GCC; see the file COPYING3.  If not see
>>> +<http://www.gnu.org/licenses/>.  */
>>> +
>>> +#ifndef GCC_SERVER_H
>>> +#define GCC_SERVER_H
>>> +
>>> +/* Wrapper aroung "int" for file descriptors.  */
>>                ~~~^
>>              around :)
> 
> Thanks; fixed in my working copy.
> 
> Someone pointed out to me privately that instead/as well as serving on
> a port, we could be launched as a subprocess by the IDE, and serve the
> RPC over stdin/stdout; this would be simpler for IDEs to cope with.  I
> may have a look at supporting that for the next version.

The security threat modeling I think is nicer if you read and write stdin/stdout.  Once you open a port, you open a security hole.  By not having holes by design, we avoid most of this space, which I see as a good thing.
Jeff Law Sept. 1, 2017, 5:09 p.m. UTC | #4
On 07/24/2017 02:05 PM, David Malcolm wrote:
> This patch adds a "server" abstract base class for listening
> on a port, for use by the later patches to implement an LSP server.
> 
> It's largely adapted from examples in glibc's docs.  I suspect that
> I've introduced platform-specific assumptions (and that it may need some
> extra configure tests for the extra functionality), but this part of
> the kit is just a proof-of-concept.
> 
> gcc/ChangeLog:
> 	* Makefile.in (OBJS): Add server.o.
> 	* server.c: New file.
> 	* server.h: New file.
And this is where I start to get scared :-)  Once folks can start
interacting with GCC over a TCP connection we have to start thinking
much harder about the security ramifications of various hunks of code.

If we end up going down this path at some point, I'd really like to look
for ways to leverage existing code that's already being used in the wild
and hopefully has been through real security audits.

Jeff
diff mbox

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 77ecbd6..4e60bc0 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1454,6 +1454,7 @@  OBJS = \
 	sel-sched.o \
 	selftest-rtl.o \
 	selftest-run-tests.o \
+	server.o \
 	sese.o \
 	shrink-wrap.o \
 	simplify-rtx.o \
diff --git a/gcc/server.c b/gcc/server.c
new file mode 100644
index 0000000..871a95f
--- /dev/null
+++ b/gcc/server.c
@@ -0,0 +1,152 @@ 
+/* Abstract server implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "server.h"
+
+/* adapted from GNU libc docs.  */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#define MAXMSG  512
+
+static int
+make_socket (uint16_t port)
+{
+  int sock;
+  struct sockaddr_in name;
+
+  /* Create the socket. */
+  sock = socket (PF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    {
+      perror ("socket");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Give the socket a name. */
+  name.sin_family = AF_INET;
+  name.sin_port = htons (port);
+  name.sin_addr.s_addr = htonl (INADDR_ANY);
+  if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
+    {
+      perror ("bind");
+      exit (EXIT_FAILURE);
+    }
+
+  return sock;
+}
+
+/* Serve on PORT.  */
+
+void
+server::serve (int port)
+{
+  int sock;
+  fd_set active_fd_set, read_fd_set;
+  int i;
+  struct sockaddr_in clientname;
+  socklen_t size;
+
+  /* Create the socket and set it up to accept connections. */
+  sock = make_socket (port);
+  if (listen (sock, 1) < 0)
+    {
+      perror ("listen");
+      exit (EXIT_FAILURE);
+    }
+
+  /* Initialize the set of active sockets. */
+  FD_ZERO (&active_fd_set);
+  FD_SET (sock, &active_fd_set);
+
+  while (1)
+    {
+      /* Block until input arrives on one or more active sockets. */
+      read_fd_set = active_fd_set;
+      if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
+	{
+	  perror ("select");
+	  exit (EXIT_FAILURE);
+	}
+
+      /* Service all the sockets with input pending. */
+      for (i = 0; i < FD_SETSIZE; ++i)
+	if (FD_ISSET (i, &read_fd_set))
+	  {
+	    if (i == sock)
+	      {
+		/* Connection request on original socket. */
+		int new_;
+		size = sizeof (clientname);
+		new_ = accept (sock,
+			       (struct sockaddr *) &clientname,
+			       &size);
+		if (new_ < 0)
+		  {
+		    perror ("accept");
+		    exit (EXIT_FAILURE);
+		  }
+		fprintf (stderr,
+			 "Server: connect from host %s, port %hd.\n",
+			 inet_ntoa (clientname.sin_addr),
+			 ntohs (clientname.sin_port));
+		FD_SET (new_, &active_fd_set);
+	      }
+	    else
+	      {
+		/* Data arriving on an already-connected socket. */
+		if (read_from_client (file_descriptor (i)) < 0)
+		  {
+		    close (i);
+		    FD_CLR (i, &active_fd_set);
+		  }
+	      }
+	  }
+    }
+}
+
+int
+server::read_from_client (file_descriptor fd)
+{
+  char buffer[MAXMSG];
+  int nbytes;
+
+  nbytes = read (fd.m_fd, buffer, MAXMSG);
+  if (nbytes < 0)
+    {
+      /* Read error. */
+      perror ("read");
+      exit (EXIT_FAILURE);
+    }
+  else if (nbytes == 0)
+    /* End-of-file. */
+    return -1;
+  else
+    {
+      /* Data read. */
+      on_read (fd, nbytes, buffer);
+      return 0;
+    }
+}
diff --git a/gcc/server.h b/gcc/server.h
new file mode 100644
index 0000000..3bcf9f6e
--- /dev/null
+++ b/gcc/server.h
@@ -0,0 +1,46 @@ 
+/* Abstract server implementation.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_SERVER_H
+#define GCC_SERVER_H
+
+/* Wrapper aroung "int" for file descriptors.  */
+
+struct file_descriptor
+{
+  explicit file_descriptor (int fd) : m_fd (fd) {}
+
+  int m_fd;
+};
+
+/* Abstract base class for implementing a server.  */
+
+class server
+{
+ public:
+  virtual ~server () {}
+  void serve (int port);
+
+  virtual void on_read (file_descriptor fd, size_t length, const char *buf) = 0;
+
+ private:
+  int read_from_client (file_descriptor fd);
+};
+
+#endif  /* GCC_SERVER_H  */