1/*
2 * Copyright (C) 2001-2005 Chris Ross, Stephan Engstrom, Alex Holden et al
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * o Redistributions of source code must retain the above copyright notice, this
9 *   list of conditions and the following disclaimer.
10 * o Redistributions in binary form must reproduce the above copyright notice,
11 *   this list of conditions and the following disclaimer in the documentation
12 *   and/or other materials provided with the distribution.
13 * o Neither the name of the ferite software nor the names of its contributors may
14 *   be used to endorse or promote products derived from this software without
15 *   specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30uses "stream";
31uses "network";
32
33module-header {
34#include "../network/util_network.h"
35}
36
37/**
38 * @namespace Network
39 * @modifies Network
40 */
41namespace modifies Network
42{
43
44    /** @namespace Unix @brief The Unix namespace provides a set of tools to access unix domain socket streams */
45    namespace Unix {
46
47        /**
48        * @class Stream
49         * @extends Stream.StdioStream
50         * @brief Provides a Unix domain socket implementation of the Stream object
51         */
52        class Stream extends Stream.StdioStream
53        {
54            /**
55             * @variable path @type string @brief The path the unix domain socket is connected on */
56            string path;
57
58            /**
59                * @function accept
60             * @declaration function accept()
61             * @brief Accept an incoming connection
62             * @description This function will accept a connection and return a
63             *              UnixStream object which can be used. This function will
64             *              only work if the socket was initially created using
65             *              Network.unix_bind().
66             */
67            native function accept()
68            {
69                FeriteVariable *obj, **args, *fv;
70                FeriteClass *cls;
71                int sock;
72
73                do
74                {
75                    sock = accept((int)SelfObj->filedata, NULL, NULL);
76                }
77                while(sock == -1 && errno == EINTR);
78
79                if(sock == -1)
80                {
81                    ferite_set_error(script, errno, "%s", strerror(errno));
82                    FE_RETURN_NULL_OBJECT;
83                }
84
85                if((cls = ferite_find_class(script, script->mainns, "Network.Unix.Stream")))
86                {
87                    args = ferite_create_parameter_list(4);
88                    args = ferite_add_to_parameter_list(args, fe_new_lng_static("socket", sock));
89                    MARK_VARIABLE_AS_DISPOSABLE(args[0]);
90                    obj = ferite_new_object(script, cls, args);
91                    ferite_delete_parameter_list(script, args);
92                    fv = ferite_object_get_var(script, self, "path");
93                    fv = ferite_create_string_variable(script, "path", VAS(fv),
94                                                       FE_STATIC);
95                    ferite_object_set_var(script, VAO(obj), "path", fv);
96                    FE_RETURN_VAR(obj);
97                }
98                FE_RETURN_NULL_OBJECT;
99            }
100
101            /**
102                * @function listen
103             * @declaration function listen(number backlog)
104             * @brief Set the number of slots the system listens on for connections.
105             * @param number backlog The number of slots
106             */
107            native function listen(number backlog)
108            {
109                int sock = (int)SelfObj->filedata;
110                int retval = listen(sock, (long)backlog);
111                FE_RETURN_LONG(retval);
112            }
113        }
114        /**
115        * @end
116         */
117
118        /**
119        * @function connect
120         * @declaration function connect(string path)
121         * @brief Connect to a Unix domain socket at the specified location
122         * @param string path The full path to the named socket
123         * @return An object connected to the named socket, or null if the
124         *         connection could not be established
125         */
126        native function connect(string path)
127        {
128            FeriteVariable *obj, **args, *fv;
129            FeriteClass *cls;
130            struct sockaddr *sa;
131            struct sockaddr_un un;
132            int sock;
133
134            if((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1)
135                FE_RETURN_NULL_OBJECT;
136
137            un.sun_family = AF_UNIX;
138            if(path->length + 1 > sizeof(un.sun_path))
139            {
140                ferite_set_error(script, 0, "Path too long");
141                FE_RETURN_NULL_OBJECT;
142            }
143            memcpy(un.sun_path, path->data, path->length + 1);
144            sa = (struct sockaddr*)&un;
145
146            if(connect(sock, sa, sizeof(struct sockaddr_un)) == -1)
147            {
148                ferite_set_error(script, errno, "%s", strerror(errno));
149                close(sock);
150                FE_RETURN_NULL_OBJECT;
151            }
152
153            if((cls = ferite_find_class(script, script->mainns, "Network.Unix.Stream")))
154            {
155                args = ferite_create_parameter_list(4);
156                args = ferite_add_to_parameter_list(args, fe_new_lng_static("socket", sock));
157                MARK_VARIABLE_AS_DISPOSABLE(args[0]); /* automatically clear up */
158                obj = ferite_new_object(script, cls, args);
159                ferite_delete_parameter_list(script, args);
160                fv = ferite_create_string_variable(script, "path", path, FE_STATIC);
161                ferite_object_set_var(script, VAO(obj), "path", fv);
162                FE_RETURN_VAR(obj);
163            }
164            FE_RETURN_NULL_OBJECT;
165        }
166
167        /**
168        * @function bind
169         * @declaration function bind(string path)
170         * @brief Bind to a Unix domain socket
171         * @param string path The full path to the file to use as the named socket
172         * @return An object that can accept connections, or null on error
173         * @description Creates a UnixStream object bound to the specified
174         *              Unix domain socket. Note that the bind will fail if the
175         *              file already exists, and Unix domain sockets are not
176         *              automatically deleted when the server closes them, so
177         *              you may need to delete a stale socket file before you can
178         *              bind to it.
179         */
180        native function bind(string path)
181        {
182            FeriteVariable *obj, **args, *fv;
183            FeriteClass *cls;
184            struct sockaddr *sa;
185            struct sockaddr_un un;
186            int sock;
187
188            if((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1)
189                FE_RETURN_NULL_OBJECT;
190
191            un.sun_family = AF_UNIX;
192            if(path->length + 1 > sizeof(un.sun_path))
193            {
194                ferite_set_error(script, 0, "Path too long");
195                FE_RETURN_NULL_OBJECT;
196            }
197            memcpy(un.sun_path, path->data, path->length + 1);
198            sa = (struct sockaddr*)&un;
199
200            if(bind(sock, sa, sizeof(struct sockaddr_un)))
201            {
202                ferite_set_error(script, errno, "%s", strerror(errno));
203                close(sock);
204                FE_RETURN_NULL_OBJECT;
205            }
206            if(listen(sock, 10))
207            {
208                ferite_set_error(script, errno, "%s", strerror(errno));
209                close(sock);
210                FE_RETURN_NULL_OBJECT;
211            }
212            if((cls = ferite_find_class(script, script->mainns, "Network.Unix.Stream")))
213            {
214                args = ferite_create_parameter_list(4);
215                args = ferite_add_to_parameter_list(args, fe_new_lng_static("socket", sock));
216                obj = ferite_new_object(script, cls, args);
217                ferite_delete_parameter_list(script, args);
218                fv = ferite_create_string_variable(script, "path", path, FE_STATIC);
219                ferite_object_set_var(script, VAO(obj), "path", fv);
220                FE_RETURN_VAR(obj);
221            }
222            close(sock);
223            FE_RETURN_NULL_OBJECT;
224        }
225    }
226    /** @end */
227}
228/**
229 *  @end
230 */