Import IPv6-supported Lua5.2 compatible code from Matthew Wild's repo

Signed-off-by: Vadim A. Misbakh-Soloviov <mva@mva.name>
This commit is contained in:
Vadim A. Misbakh-Soloviov 2012-05-09 17:58:51 +07:00
parent 40f3ea55e3
commit 3f3c2bf818
106 changed files with 15244 additions and 1 deletions

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
LuaSocket 2.0.2 license
Copyright © 2004-2007 Diego Nehab
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

38
NEW Normal file
View File

@ -0,0 +1,38 @@
What's New
This is just a bug-fix/update release.
* Fixed: manual sample of HTTP authentication now uses correct
"authorization" header (Alexandre Ittner);
* Fixed: failure on bind() was destroying the socket (Sam Roberts);
* Fixed: receive() returns immediatelly if prefix can satisfy
bytes requested (M Joonas Pihlaja);
* Fixed: multicast didn't work on Windows, or anywhere
else for that matter (Herbert Leuwer, Adrian Sietsma);
* Fixed: select() now reports an error when called with more
sockets than FD_SETSIZE (Lorenzo Leonini);
* Fixed: manual links to home.html changed to index.html (Robert Hahn);
* Fixed: mime.unb64() would return an empty string on results that started
with a null character (Robert Raschke);
* Fixed: HTTP now automatically redirects on 303 and 307 (Jonathan Gray);
* Fixed: calling sleep() with negative numbers could
block forever, wasting CPU. Now it returns immediately (MPB);
* Improved: FTP commands are now sent in upper case to
help buggy servers (Anders Eurenius);
* Improved: known headers now sent in canonic
capitalization to help buggy servers (Joseph Stewart);
* Improved: Clarified tcp:receive() in the manual (MPB);
* Improved: Decent makefiles (LHF).
* Fixed: RFC links in documentation now point to IETF (Cosmin Apreutesei).
Yuri's bug?
Dahlberg
Sam Roberts
Thomas Harning Jr.
Sebastien Perin
remove getn in all files
ltn12.pump.all(
ltn12.source.file(io.open("original.png")),
ltn12.sink.file(io.open("copy.png", "wb"))
)

7
README
View File

@ -1 +1,6 @@
Alright, this is the first file I add to git. This is the LuaSocket 2.1.1. It has been tested on --[[WinXP--]], Mac OS X,
and --[[Linux--]]. Please use the Lua mailing list to report any bugs
(or "features") you encounter.
Have fun,
Diego Nehab.

132
doc/dns.html Normal file
View File

@ -0,0 +1,132 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: DNS support">
<meta name="keywords" content="Lua, LuaSocket, DNS, Network, Library, Support">
<title>LuaSocket: DNS support</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- dns ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=dns>DNS</h2>
<p>
Name resolution functions return <em>all</em> information obtained from the
resolver in a table of the form:
</p>
<blockquote><tt>
resolved = {<br>
&nbsp;&nbsp;name = <i>canonic-name</i>,<br>
&nbsp;&nbsp;alias = <i>alias-list</i>,<br>
&nbsp;&nbsp;ip = <i>ip-address-list</i><br>
}
</tt> </blockquote>
<p>
Note that the <tt>alias</tt> list can be empty.
</p>
<!-- gethostname ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=gethostname>
socket.dns.<b>gethostname()</b>
</p>
<p class=description>
Returns the standard host name for the machine as a string.
</p>
<!-- tohostname +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=tohostname>
socket.dns.<b>tohostname(</b>address<b>)</b>
</p>
<p class=description>
Converts from IP address to host name.
</p>
<p class=parameters>
<tt>Address</tt> can be an IP address or host name.
</p>
<p class=return>
The function returns a string with the canonic host name of the given
<tt>address</tt>, followed by a table with all information returned by
the resolver. In case of error, the function returns <b><tt>nil</tt></b>
followed by an error message.
</p>
<!-- toip +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=toip>
socket.dns.<b>toip(</b>address<b>)</b>
</p>
<p class=description>
Converts from host name to IP address.
</p>
<p class=parameters>
<tt>Address</tt> can be an IP address or host name.
</p>
<p class=return>
Returns a string with the first IP address found for <tt>address</tt>,
followed by a table with all information returned by the resolver.
In case of error, the function returns <b><tt>nil</tt></b> followed by an error
message.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#down">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:07 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

289
doc/ftp.html Normal file
View File

@ -0,0 +1,289 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: FTP support">
<meta name="keywords" content="Lua, LuaSocket, FTP, Network, Library, Support">
<title>LuaSocket: FTP support</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- ftp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=ftp>FTP</h2>
<p>
FTP (File Transfer Protocol) is a protocol used to transfer files
between hosts. The <tt>ftp</tt> namespace offers thorough support
to FTP, under a simple interface. The implementation conforms to
<a href="http://www.ietf.org/rfc/rfc959.txt">RFC 959</a>.
</p>
<p>
High level functions are provided supporting the most common operations.
These high level functions are implemented on top of a lower level
interface. Using the low-level interface, users can easily create their
own functions to access <em>any</em> operation supported by the FTP
protocol. For that, check the implementation.
</p>
<p>
To really benefit from this module, a good understanding of
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
LTN012, Filters sources and sinks</a> is necessary.
</p>
<p>
To obtain the <tt>ftp</tt> namespace, run:
</p>
<pre class=example>
-- loads the FTP module and any libraries it requires
local ftp = require("socket.ftp")
</pre>
<p>
URLs MUST conform to
<a href="http://www.ietf.org/rfc/rfc1738.txt">RFC 1738</a>,
that is, an URL is a string in the form:
</p>
<blockquote>
<tt>
[ftp://][&lt;user&gt;[:&lt;password&gt;]@]&lt;host&gt;[:&lt;port&gt;][/&lt;path&gt;][<i>type</i>=a|i]</tt>
</blockquote>
<p>
The following constants in the namespace can be set to control the default behavior of
the FTP module:
</p>
<ul>
<li> <tt>PASSWORD</tt>: default anonymous password.
<li> <tt>PORT</tt>: default port used for the control connection;
<li> <tt>TIMEOUT</tt>: sets the timeout for all I/O operations;
<li> <tt>USER</tt>: default anonymous user;
</ul>
<!-- ftp.get ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=get>
ftp.<b>get(</b>url<b>)</b><br>
ftp.<b>get{</b><br>
&nbsp;&nbsp;host = <i>string</i>,<br>
&nbsp;&nbsp;sink = <i>LTN12 sink</i>,<br>
&nbsp;&nbsp;argument <i>or</i> path = <i>string</i>,<br>
&nbsp;&nbsp;[user = <i>string</i>,]<br>
&nbsp;&nbsp;[password = <i>string</i>]<br>
&nbsp;&nbsp;[command = <i>string</i>,]<br>
&nbsp;&nbsp;[port = <i>number</i>,]<br>
&nbsp;&nbsp;[type = <i>string</i>,]<br>
&nbsp;&nbsp;[step = <i>LTN12 pump step</i>,]<br>
&nbsp;&nbsp;[create = <i>function</i>]<br>
<b>}</b>
</p>
<p class=description>
The <tt>get</tt> function has two forms. The simple form has fixed
functionality: it downloads the contents of a URL and returns it as a
string. The generic form allows a <em>lot</em> more control, as explained
below.
</p>
<p class=parameters>
If the argument of the <tt>get</tt> function is a table, the function
expects at least the fields <tt>host</tt>, <tt>sink</tt>, and one of
<tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes
precedence). <tt>Host</tt> is the server to connect to. <tt>Sink</tt> is
the <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
sink that will receive the downloaded data. <tt>Argument</tt> or
<tt>path</tt> give the target path to the resource in the server. The
optional arguments are the following:
</p>
<ul>
<li><tt>user</tt>, <tt>password</tt>: User name and password used for
authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>";
<li><tt>command</tt>: The FTP command used to obtain data. Defaults to
"<tt>retr</tt>", but see example below;
<li><tt>port</tt>: The port to used for the control connection. Defaults to 21;
<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or
"<tt>a</tt>". Defaults to whatever is the server default;
<li><tt>step</tt>:
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
pump step function used to pass data from the
server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function;
<li><tt>create</tt>: An optional function to be used instead of
<a href=tcp.html#socket.tcp><tt>socket.tcp</tt></a> when the communications socket is created.
</ul>
<p class=return>
If successful, the simple version returns the URL contents as a
string, and the generic function returns 1. In case of error, both
functions return <b><tt>nil</tt></b> and an error message describing the
error.
</p>
<pre class=example>
-- load the ftp support
local ftp = require("socket.ftp")
-- Log as user "anonymous" on server "ftp.tecgraf.puc-rio.br",
-- and get file "lua.tar.gz" from directory "pub/lua" as binary.
f, e = ftp.get("ftp://ftp.tecgraf.puc-rio.br/pub/lua/lua.tar.gz;type=i")
</pre>
<pre class=example>
-- load needed modules
local ftp = require("socket.ftp")
local ltn12 = require("ltn12")
local url = require("socket.url")
-- a function that returns a directory listing
function nlst(u)
local t = {}
local p = url.parse(u)
p.command = "nlst"
p.sink = ltn12.sink.table(t)
local r, e = ftp.get(p)
return r and table.concat(t), e
end
</pre>
<!-- put ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=put>
ftp.<b>put(</b>url, content<b>)</b><br>
ftp.<b>put{</b><br>
&nbsp;&nbsp;host = <i>string</i>,<br>
&nbsp;&nbsp;source = <i>LTN12 sink</i>,<br>
&nbsp;&nbsp;argument <i>or</i> path = <i>string</i>,<br>
&nbsp;&nbsp;[user = <i>string</i>,]<br>
&nbsp;&nbsp;[password = <i>string</i>]<br>
&nbsp;&nbsp;[command = <i>string</i>,]<br>
&nbsp;&nbsp;[port = <i>number</i>,]<br>
&nbsp;&nbsp;[type = <i>string</i>,]<br>
&nbsp;&nbsp;[step = <i>LTN12 pump step</i>,]<br>
&nbsp;&nbsp;[create = <i>function</i>]<br>
<b>}</b>
</p>
<p class=description>
The <tt>put</tt> function has two forms. The simple form has fixed
functionality: it uploads a string of content into a URL. The generic form
allows a <em>lot</em> more control, as explained below.
</p>
<p class=parameters>
If the argument of the <tt>put</tt> function is a table, the function
expects at least the fields <tt>host</tt>, <tt>source</tt>, and one of
<tt>argument</tt> or <tt>path</tt> (<tt>argument</tt> takes
precedence). <tt>Host</tt> is the server to connect to. <tt>Source</tt> is
the <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
source that will provide the contents to be uploaded.
<tt>Argument</tt> or
<tt>path</tt> give the target path to the resource in the server. The
optional arguments are the following:
</p>
<ul>
<li><tt>user</tt>, <tt>password</tt>: User name and password used for
authentication. Defaults to "<tt>ftp:anonymous@anonymous.org</tt>";
<li><tt>command</tt>: The FTP command used to send data. Defaults to
"<tt>stor</tt>", but see example below;
<li><tt>port</tt>: The port to used for the control connection. Defaults to 21;
<li><tt>type</tt>: The transfer mode. Can take values "<tt>i</tt>" or
"<tt>a</tt>". Defaults to whatever is the server default;
<li><tt>step</tt>:
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
pump step function used to pass data from the
server to the sink. Defaults to the LTN12 <tt>pump.step</tt> function;
<li><tt>create</tt>: An optional function to be used instead of
<a href=tcp.html#socket.tcp><tt>socket.tcp</tt></a> when the communications socket is created.
</ul>
<p class=return>
Both functions return 1 if successful, or <b><tt>nil</tt></b> and an error
message describing the reason for failure.
</p>
<pre class=example>
-- load the ftp support
local ftp = require("socket.ftp")
-- Log as user "fulano" on server "ftp.example.com",
-- using password "silva", and store a file "README" with contents
-- "wrong password, of course"
f, e = ftp.put("ftp://fulano:silva@ftp.example.com/README",
"wrong password, of course")
</pre>
<pre class=example>
-- load the ftp support
local ftp = require("socket.ftp")
local ltn12 = require("ltn12")
-- Log as user "fulano" on server "ftp.example.com",
-- using password "silva", and append to the remote file "LOG", sending the
-- contents of the local file "LOCAL-LOG"
f, e = ftp.put{
host = "ftp.example.com",
user = "fulano",
password = "silva",
command = "appe",
argument = "LOG",
source = ltn12.source.file(io.open("LOCAL-LOG", "r"))
}
</pre>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:18 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

330
doc/http.html Normal file
View File

@ -0,0 +1,330 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: HTTP support">
<meta name="keywords" content="Lua, HTTP, Library, WWW, Browser, Network, Support">
<title>LuaSocket: HTTP support</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- http +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=http>HTTP</h2>
<p>
HTTP (Hyper Text Transfer Protocol) is the protocol used to exchange
information between web-browsers and servers. The <tt>http</tt>
namespace offers full support for the client side of the HTTP
protocol (i.e.,
the facilities that would be used by a web-browser implementation). The
implementation conforms to the HTTP/1.1 standard,
<a href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a>.
</p>
<p>
The module exports functions that provide HTTP functionality in different
levels of abstraction. From the simple
string oriented requests, through generic
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> based, down to even lower-level if you bother to look through the source code.
</p>
<p>
To obtain the <tt>http</tt> namespace, run:
</p>
<pre class=example>
-- loads the HTTP module and any libraries it requires
local http = require("socket.http")
</pre>
<p>
URLs must conform to
<a href="http://www.ietf.org/rfc/rfc1738.txt">RFC 1738</a>,
that is, an URL is a string in the form:
</p>
<blockquote>
<pre>
[http://][&lt;user&gt;[:&lt;password&gt;]@]&lt;host&gt;[:&lt;port&gt;][/&lt;path&gt;]
</pre>
</blockquote>
<p>
MIME headers are represented as a Lua table in the form:
</p>
<blockquote>
<table summary="MIME headers in Lua table">
<tr><td><tt>
headers = {<br>
&nbsp;&nbsp;field-1-name = <i>field-1-value</i>,<br>
&nbsp;&nbsp;field-2-name = <i>field-2-value</i>,<br>
&nbsp;&nbsp;field-3-name = <i>field-3-value</i>,<br>
&nbsp;&nbsp;...<br>
&nbsp;&nbsp;field-n-name = <i>field-n-value</i><br>
}
</tt></td></tr>
</table>
</blockquote>
<p>
Field names are case insensitive (as specified by the standard) and all
functions work with lowercase field names.
Field values are left unmodified.
</p>
<p class=note>
Note: MIME headers are independent of order. Therefore, there is no problem
in representing them in a Lua table.
</p>
<p>
The following constants can be set to control the default behavior of
the HTTP module:
</p>
<ul>
<li> <tt>PORT</tt>: default port used for connections;
<li> <tt>PROXY</tt>: default proxy used for connections;
<li> <tt>TIMEOUT</tt>: sets the timeout for all I/O operations;
<li> <tt>USERAGENT</tt>: default user agent reported to server.
</ul>
<!-- http.request ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=request>
http.<b>request(</b>url [, body]<b>)</b><br>
http.<b>request{</b><br>
&nbsp;&nbsp;url = <i>string</i>,<br>
&nbsp;&nbsp;[sink = <i>LTN12 sink</i>,]<br>
&nbsp;&nbsp;[method = <i>string</i>,]<br>
&nbsp;&nbsp;[headers = <i>header-table</i>,]<br>
&nbsp;&nbsp;[source = <i>LTN12 source</i>],<br>
&nbsp;&nbsp;[step = <i>LTN12 pump step</i>,]<br>
&nbsp;&nbsp;[proxy = <i>string</i>,]<br>
&nbsp;&nbsp;[redirect = <i>boolean</i>,]<br>
&nbsp;&nbsp;[create = <i>function</i>]<br>
<b>}</b>
</p>
<p class=description>
The request function has two forms. The simple form downloads
a URL using the <tt>GET</tt> or <tt>POST</tt> method and is based
on strings. The generic form performs any HTTP method and is
<a href=http://lua-users.org/wiki/FiltersSourcesAndSinks>LTN12</a> based.
</p>
<p class=parameters>
If the first argument of the <tt>request</tt> function is a string, it
should be an <tt>url</tt>. In that case, if a <tt>body</tt>
is provided as a string, the function will perform a <tt>POST</tt> method
in the <tt>url</tt>. Otherwise, it performs a <tt>GET</tt> in the
<tt>url</tt>
</p>
<p class=parameters>
If the first argument is instead a table, the most important fields are
the <tt>url</tt> and the <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<tt>sink</tt> that will receive the downloaded content.
Any part of the <tt>url</tt> can be overridden by including
the appropriate field in the request table.
If authentication information is provided, the function
uses the Basic Authentication Scheme (see <a href="#authentication">note</a>)
to retrieve the document. If <tt>sink</tt> is <tt><b>nil</b></tt>, the
function discards the downloaded data. The optional parameters are the
following:
</p>
<ul>
<li><tt>method</tt>: The HTTP request method. Defaults to "GET";
<li><tt>headers</tt>: Any additional HTTP headers to send with the request;
<li><tt>source</tt>: <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
source to provide the request body. If there
is a body, you need to provide an appropriate "<tt>content-length</tt>"
request header field, or the function will attempt to send the body as
"<tt>chunked</tt>" (something few servers support). Defaults to the empty source;
<li><tt>step</tt>:
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
pump step function used to move data.
Defaults to the LTN12 <tt>pump.step</tt> function.
<li><tt>proxy</tt>: The URL of a proxy server to use. Defaults to no proxy;
<li><tt>redirect</tt>: Set to <tt><b>false</b></tt> to prevent the
function from automatically following 301 or 302 server redirect messages;
<li><tt>create</tt>: An optional function to be used instead of
<a href=tcp.html#socket.tcp><tt>socket.tcp</tt></a> when the communications socket is created.
</ul>
<p class=return>
In case of failure, the function returns <tt><b>nil</b></tt> followed by an
error message. If successful, the simple form returns the response
body as a string, followed by the response status code, the response
headers and the response status line. The generic function returns the same
information, except the first return value is just the number 1 (the body
goes to the <tt>sink</tt>).
</p>
<p class=return>
Even when the server fails to provide the contents of the requested URL (URL not found, for example),
it usually returns a message body (a web page informing the
URL was not found or some other useless page). To make sure the
operation was successful, check the returned status <tt>code</tt>. For
a list of the possible values and their meanings, refer to <a
href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a>.
</p>
<p class=description>
Here are a few examples with the simple interface:
</p>
<pre class=example>
-- load the http module
local io = require("io")
local http = require("socket.http")
local ltn12 = require("ltn12")
-- connect to server "www.cs.princeton.edu" and retrieves this manual
-- file from "~diego/professional/luasocket/http.html" and print it to stdout
http.request{
url = "http://www.cs.princeton.edu/~diego/professional/luasocket/http.html",
sink = ltn12.sink.file(io.stdout)
}
-- connect to server "www.example.com" and tries to retrieve
-- "/private/index.html". Fails because authentication is needed.
b, c, h = http.request("http://www.example.com/private/index.html")
-- b returns some useless page telling about the denied access,
-- h returns authentication information
-- and c returns with value 401 (Authentication Required)
-- tries to connect to server "wrong.host" to retrieve "/"
-- and fails because the host does not exist.
r, e = http.request("http://wrong.host/")
-- r is nil, and e returns with value "host not found"
</pre>
<p class=description>
And here is an example using the generic interface:
</p>
<pre class=example>
-- load the http module
http = require("socket.http")
-- Requests information about a document, without downloading it.
-- Useful, for example, if you want to display a download gauge and need
-- to know the size of the document in advance
r, c, h = http.request {
method = "HEAD",
url = "http://www.tecgraf.puc-rio.br/~diego"
}
-- r is 1, c is 200, and h would return the following headers:
-- h = {
-- date = "Tue, 18 Sep 2001 20:42:21 GMT",
-- server = "Apache/1.3.12 (Unix) (Red Hat/Linux)",
-- ["last-modified"] = "Wed, 05 Sep 2001 06:11:20 GMT",
-- ["content-length"] = 15652,
-- ["connection"] = "close",
-- ["content-Type"] = "text/html"
-- }
</pre>
<p class=note id=post>
Note: When sending a POST request, simple interface adds a
"<tt>Content-type: application/x-www-form-urlencoded</tt>"
header to the request. This is the type used by
HTML forms. If you need another type, use the generic
interface.
</p>
<p class=note id=authentication>
Note: Some URLs are protected by their
servers from anonymous download. For those URLs, the server must receive
some sort of authentication along with the request or it will deny
download and return status "401&nbsp;Authentication Required".
</p>
<p class=note>
The HTTP/1.1 standard defines two authentication methods: the Basic
Authentication Scheme and the Digest Authentication Scheme, both
explained in detail in
<a href="http://www.ietf.org/rfc/rfc2068.txt">RFC 2068</a>.
</p>
<p class=note>The Basic Authentication Scheme sends
<tt>&lt;user&gt;</tt> and
<tt>&lt;password&gt;</tt> unencrypted to the server and is therefore
considered unsafe. Unfortunately, by the time of this implementation,
the wide majority of servers and browsers support the Basic Scheme only.
Therefore, this is the method used by the toolkit whenever
authentication is required.
</p>
<pre class=example>
-- load required modules
http = require("socket.http")
mime = require("mime")
-- Connect to server "www.example.com" and tries to retrieve
-- "/private/index.html", using the provided name and password to
-- authenticate the request
b, c, h = http.request("http://fulano:silva@www.example.com/private/index.html")
-- Alternatively, one could fill the appropriate header and authenticate
-- the request directly.
r, c = http.request {
url = "http://www.example.com/private/index.html",
headers = { authorization = "Basic " .. (mime.b64("fulano:silva")) }
}
</pre>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:26 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

195
doc/index.html Normal file
View File

@ -0,0 +1,195 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="The LuaSocket Homepage">
<meta name="keywords" content="Lua, LuaSocket, Network, Library, Support, Internet">
<title>LuaSocket: Network support for the Lua language </title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- whatis +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=whatis>What is LuaSocket?</h2>
<p>
LuaSocket is a <a href="http://www.lua.org">Lua</a> extension library
that is composed by two parts: a C core that provides support for the TCP
and UDP transport layers, and a set of Lua modules that add support for
functionality commonly needed by applications that deal with the Internet.
</p>
<p>
The core support has been implemented so that it is both efficient and
simple to use. It is available to any Lua application once it has been
properly initialized by the interpreter in use. The code has been tested
and runs well on several Windows and Unix platforms. </p>
<p>
Among the support modules, the most commonly used implement the
<a href=smtp.html>SMTP</a>
(sending e-mails),
<a href=http.html>HTTP</a>
(WWW access) and
<a href=ftp.html>FTP</a>
(uploading and downloading files) client
protocols. These provide a very natural and generic interface to the
functionality defined by each protocol.
In addition, you will find that the
<a href=mime.html>MIME</a> (common encodings),
<a href=url.html>URL</a>
(anything you could possible want to do with one) and
<a href=ltn12.html>LTN12</a>
(filters, sinks, sources and pumps) modules can be very handy.
</p>
<p>
The library is available under the same
<a href="http://www.lua.org/copyright.html">
terms and conditions</a> as the Lua language, the MIT license. The idea is
that if you can use Lua in a project, you should also be able to use
LuaSocket.
</p>
<p>
Copyright &copy; 2004-2007 Diego Nehab. All rights reserved. <br>
Author: <A href="http://www.cs.princeton.edu/~diego">Diego Nehab</a>
</p>
<!-- download +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=download>Download</h2>
<p>
LuaSocket version 2.0.3 is now available for download! It is
compatible with Lua&nbsp;5.1, and has
been tested on Windows&nbsp;XP, Linux, and Mac OS X. Chances
are it works well on most UNIX distributions and Windows flavors.
</p>
<p>
The library can be downloaded in source code from the
<a href=http://luaforge.net/projects/luasocket/>LuaSocket
project page</a> at LuaForge.
Besides the full C and Lua source code for the library, the distribution
contains several examples, this user's manual and basic test procedures.
</p>
<p>
Danilo Tuler is maintaining Win32 binaries for LuaSocket, which are also
available from LuaForge. These are compatible with the
<a href=http://luaforge.net/projects/luabinaries>LuaBinaries</a>,
also available from LuaForge.
</p>
<p> Take a look at the <a
href=installation.html>installation</a> section of the
manual to find out how to properly install the library.
</p>
<!-- thanks +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=thanks>Special thanks</h2>
<p>
Throughout LuaSocket's history, many people gave suggestions
that helped improve it. For that, I thank the Lua community.
Special thanks go to David Burgess, who has helped push the
library to a new level of quality and from whom I have
learned a lot of stuff that doesn't show up in RFCs.
Special thanks also to Carlos Cassino, who played a big part
in the extensible design seen in the C core of LuaSocket
2.0. Mike Pall has been helping a lot too! Thanks to you
all!
</p>
<!-- whatsnew +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=new>What's New</h2>
<p>
2.0.3 is just a bug-fix/update release.
</p>
<ul>
<li> Fixed: manual sample of HTTP authentication now uses correct
"authorization" header (Alexandre Ittner);
<li> Fixed: receive() returns immediatelly if prefix can satisfy
bytes requested (M Joonas Pihlaja);
<li> Fixed: multicast didn't work on Windows, or anywhere
else for that matter (Herbert Leuwer, Adrian Sietsma);
<li> Fixed: select() now reports an error when called with more
sockets than FD_SETSIZE (Lorenzo Leonini);
<li> Fixed: manual links to home.html changed to index.html (Robert Hahn);
<li> Fixed: mime.unb64() would return an empty string on results that started
with a null character (Robert Raschke);
<li> Fixed: HTTP now automatically redirects on 303 and 307 (Jonathan Gray);
<li> Fixed: calling sleep() with negative numbers could
block forever, wasting CPU. Now it returns immediately (MPB);
<li> Improved: FTP commands are now sent in upper case to
help buggy servers (Anders Eurenius);
<li> Improved: known headers now sent in canonic
capitalization to help buggy servers (Joseph Stewart);
<li> Improved: Clarified tcp:receive() in the manual (MPB);
<li> Improved: Decent makefiles (LHF).
</ul>
<!-- old ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=old>Old Versions</h2>
<p>
All previous versions of the LuaSocket library can be downloaded <a
href="http://www.cs.princeton.edu/~diego/professional/luasocket/old">
here</a>. Although these versions are no longer supported, they are
still available for those that have compatibility issues.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Wed Oct 3 02:07:59 BRT 2007
</small>
</p>
</center>
</div>
</body>
</html>

163
doc/installation.html Normal file
View File

@ -0,0 +1,163 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: Introduction to the core">
<meta name="keywords" content="Lua, LuaSocket, TCP, UDP, Network, Support,
Installation">
<title>LuaSocket: Installation</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- installation ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2>Installation</h2>
<p> LuaSocket 2.0.2 uses the new package system for Lua 5.1.
All Lua library developers are encouraged to update their libraries so that
all libraries can coexist peacefully and users can benefit from the
standardization and flexibility of the standard.
</p>
<p>
Those stuck with Lua 5.0 will need the
<a href=http://www.keplerproject.org/compat/>compat-5.1</a>
module. It is maintained by
<a href=http://www.keplerproject.org/>The Kepler
Project</a>'s team, and implements the Lua 5.1 package proposal
on top of Lua 5.0. </p>
<p> Here we will only describe the standard distribution.
If the standard doesn't meet your needs, we refer you to the
Lua discussion list, where any question about the package
scheme will likely already have been answered. </p>
<h3>Directory structure</h3>
<p> On Unix systems, the standard distribution uses two base
directories, one for system dependent files, and another for system
independent files. Let's call these directories <tt>&lt;CDIR&gt;</tt>
and <tt>&lt;LDIR&gt;</tt>, respectively.
For instance, in my laptop, I use '<tt>/usr/local/lib/lua/5.0</tt>' for
<tt>&lt;CDIR&gt;</tt> and '<tt>/usr/local/share/lua/5.0</tt>' for
<tt>&lt;LDIR&gt;</tt>. On Windows, sometimes only one directory is used, say
'<tt>c:\program files\lua\5.0</tt>'. Here is the standard LuaSocket
distribution directory structure:</p>
<pre class=example>
&lt;LDIR&gt;/compat-5.1.lua
&lt;LDIR&gt;/ltn12.lua
&lt;LDIR&gt;/socket.lua
&lt;CDIR&gt;/socket/core.dll
&lt;LDIR&gt;/socket/http.lua
&lt;LDIR&gt;/socket/tp.lua
&lt;LDIR&gt;/socket/ftp.lua
&lt;LDIR&gt;/socket/smtp.lua
&lt;LDIR&gt;/socket/url.lua
&lt;LDIR&gt;/mime.lua
&lt;CDIR&gt;/mime/core.dll
</pre>
<p> Naturally, on Unix systems, <tt>core.dll</tt>
would be replaced by <tt>core.so</tt>.
</p>
<p> In order for the interpreter to find all LuaSocket components, three
environment variables need to be set. The first environment variable tells
the interpreter to load the <tt>compat-5.1.lua</tt> module at startup: </p>
<pre class=example>
LUA_INIT=@&lt;LDIR&gt;/compat-5.1.lua
</pre>
<p>
This is only need for Lua&nbsp;5.0! Lua&nbsp;5.1 comes with
the package system built in, of course.
</p>
<p>
The other two environment variables instruct the compatibility module to
look for dynamic libraries and extension modules in the appropriate
directories and with the appropriate filename extensions.
</p>
<pre class=example>
LUA_PATH=&lt;LDIR&gt;/?.lua;?.lua
LUA_CPATH=&lt;CDIR&gt;/?.dll;?.dll
</pre>
<p> Again, naturally, on Unix systems the shared library extension would be
<tt>.so</tt> instead of <tt>.dll</tt>.</p>
<h3>Using LuaSocket</h3>
<p> With the above setup, and an interpreter with shared library support,
it should be easy to use LuaSocket. Just fire the interpreter and use the
<tt>require</tt> function to gain access to whatever module you need:</p>
<pre class=example>
Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio
&gt; socket = require("socket")
&gt; print(socket._VERSION)
--&gt; LuaSocket 2.0.2
</pre>
<p> Each module loads their dependencies automatically, so you only need to
load the modules you directly depend upon: </p>
<pre class=example>
Lua 5.1.2 Copyright (C) 1994-2007 Lua.org, PUC-Rio
&gt; http = require("socket.http")
&gt; print(http.request("http://www.cs.princeton.edu/~diego/professional/luasocket"))
--&gt; homepage gets dumped to terminal
</pre>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#down">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:30 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

333
doc/introduction.html Normal file
View File

@ -0,0 +1,333 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: Introduction to the core">
<meta name="keywords" content="Lua, LuaSocket, TCP, UDP, Network,
Library, Support">
<title>LuaSocket: Introduction to the core</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- introduction +++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2>Introduction</h2>
<p>
LuaSocket is a <a href="http://www.lua.org">Lua</a> extension library
that is composed by two parts: a C core that provides support for the TCP
and UDP transport layers, and a set of Lua modules that add support for
the SMTP (sending e-mails), HTTP (WWW access) and FTP (uploading and
downloading files) protocols and other functionality commonly needed by
applications that deal with the Internet. This introduction is about the C
core.
</p>
<p>
Communication in LuaSocket is performed via I/O objects. These can
represent different network domains. Currently, support is provided for TCP
and UDP, but nothing prevents other developers from implementing SSL, Local
Domain, Pipes, File Descriptors etc. I/O objects provide a standard
interface to I/O across different domains and operating systems.
</p>
<p>
The API design had two goals in mind. First, users
experienced with the C API to sockets should feel comfortable using LuaSocket.
Second, the simplicity and the feel of the Lua language should be
preserved. To achieve these goals, the LuaSocket API keeps the function names and semantics the C API whenever possible, but their usage in Lua has been greatly simplified.
</p>
<p>
One of the simplifications is the receive pattern capability.
Applications can read data from stream domains (such as TCP)
line by line, block by block, or until the connection is closed.
All I/O reads are buffered and the performance differences between
different receive patterns are negligible.
</p>
<p>
Another advantage is the flexible timeout control
mechanism. As in C, all I/O operations are blocking by default. For
example, the <a href=tcp.html#send><tt>send</tt></a>,
<a href=tcp.html#receive><tt>receive</tt></a> and
<a href=tcp.html#accept><tt>accept</tt></a> methods
of the TCP domain will block the caller application until
the operation is completed (if ever!). However, with a call to the
<a href=tcp.html#settimeout><tt>settimeout</tt></a>
method, an application can specify upper limits on
the time it can be blocked by LuaSocket (the "<tt>total</tt>" timeout), on
the time LuaSocket can internally be blocked by any OS call (the
"<tt>block</tt>" timeout) or a combination of the two. Each LuaSocket
call might perform several OS calls, so that the two timeout values are
<em>not</em> equivalent.
</p>
<p>
Finally, the host name resolution is transparent, meaning that most
functions and methods accept both IP addresses and host names. In case a
host name is given, the library queries the system's resolver and
tries the main IP address returned. Note that direct use of IP addresses
is more efficient, of course. The
<a href=dns.html#toip><tt>toip</tt></a>
and <a href=dns.html#tohostname><tt>tohostname</tt></a>
functions from the DNS module are provided to convert between host names and IP addresses.
</p>
<p>
Together, these changes make network programming in LuaSocket much simpler
than it is in C, as the following sections will show.
</p>
<!-- tcp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id=tcp>TCP</h3>
<p>
TCP (Transfer Control Protocol) is reliable stream protocol. In other
words, applications communicating through TCP can send and receive data as
an error free stream of bytes. Data is split in one end and
reassembled transparently on the other end. There are no boundaries in
the data transfers. The library allows users to read data from the
sockets in several different granularities: patterns are available for
lines, arbitrary sized blocks or "read up to connection closed", all with
good performance.
</p>
<p>
The library distinguishes three types of TCP sockets: <em>master</em>,
<em>client</em> and <em>server</em> sockets.
</p>
<p>
Master sockets are newly created TCP sockets returned by the function
<a href=tcp.html#tcp><tt>socket.tcp</tt></a>. A master socket is
transformed into a server socket
after it is associated with a <em>local</em> address by a call to the
<a href=tcp.html#bind><tt>bind</tt></a> method followed by a call to the
<a href=tcp.html#listen><tt>listen</tt></a>. Conversely, a master socket
can be changed into a client socket with the method
<a href=tcp.html#connect><tt>connect</tt></a>,
which associates it with a <em>remote</em> address.
</p>
<p>
On server sockets, applications can use the
<a href=tcp.html#accept><tt>accept</tt></a> method
to wait for a client connection. Once a connection is established, a
client socket object is returned representing this connection. The
other methods available for server socket objects are
<a href=tcp.html#getsockname><tt>getsockname</tt></a>,
<a href=tcp.html#setoption><tt>setoption</tt></a>,
<a href=tcp.html#settimeout><tt>settimeout</tt></a>, and
<a href=tcp.html#close><tt>close</tt></a>.
</p>
<p>
Client sockets are used to exchange data between two applications over
the Internet. Applications can call the methods
<a href=tcp.html#send><tt>send</tt></a> and
<a href=tcp.html#receive><tt>receive</tt></a>
to send and receive data. The other methods
available for client socket objects are
<a href=tcp.html#getsockname><tt>getsockname</tt></a>,
<a href=tcp.html#getpeername><tt>getpeername</tt></a>,
<a href=tcp.html#setoption><tt>setoption</tt></a>,
<a href=tcp.html#settimeout><tt>settimeout</tt></a>,
<a href=tcp.html#shutdown><tt>shutdown</tt></a>, and
<a href=tcp.html#close><tt>close</tt></a>.
</p>
<p>
Example:
</p>
<blockquote>
<p>
A simple echo server, using LuaSocket. The program binds to an ephemeral
port (one that is chosen by the operating system) on the local host and
awaits client connections on that port. When a connection is established,
the program reads a line from the remote end and sends it back, closing
the connection immediately. You can test it using the telnet
program.
</p>
<pre class=example>
-- load namespace
local socket = require("socket")
-- create a TCP socket and bind it to the local host, at any port
local server = assert(socket.bind("*", 0))
-- find out which port the OS chose for us
local ip, port = server:getsockname()
-- print a message informing what's up
print("Please telnet to localhost on port " .. port)
print("After connecting, you have 10s to enter a line to be echoed")
-- loop forever waiting for clients
while 1 do
-- wait for a connection from any client
local client = server:accept()
-- make sure we don't block waiting for this client's line
client:settimeout(10)
-- receive the line
local line, err = client:receive()
-- if there was no error, send it back to the client
if not err then client:send(line .. "\n") end
-- done with client, close the object
client:close()
end
</pre>
</blockquote>
<!-- udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id=udp>UDP</h3>
<p>
UDP (User Datagram Protocol) is a non-reliable datagram protocol. In
other words, applications communicating through UDP send and receive
data as independent blocks, which are not guaranteed to reach the other
end. Even when they do reach the other end, they are not guaranteed to be
error free. Data transfers are atomic, one datagram at a time. Reading
only part of a datagram discards the rest, so that the following read
operation will act on the next datagram. The advantages are in
simplicity (no connection setup) and performance (no error checking or
error correction).
</p>
<p>
Note that although no guarantees are made, these days
networks are so good that, under normal circumstances, few errors
happen in practice.
</p>
<p>
An UDP socket object is created by the
<a href=udp.html#udp><tt>socket.udp</tt></a> function. UDP
sockets do not need to be connected before use. The method
<a href=udp.html#sendto><tt>sendto</tt></a>
can be used immediately after creation to
send a datagram to IP address and port. Host names are not allowed
because performing name resolution for each packet would be forbiddingly
slow. Methods
<a href=udp.html#receive><tt>receive</tt></a> and
<a href=udp.html#receivefrom><tt>receivefrom</tt></a>
can be used to retrieve datagrams, the latter returning the IP and port of
the sender as extra return values (thus being slightly less
efficient).
</p>
<p>
When communication is performed repeatedly with a single peer, an
application should call the
<a href=udp.html#setpeername><tt>setpeername</tt></a> method to specify a
permanent partner. Methods
<a href=udp.html#sendto><tt>sendto</tt></a> and
<a href=udp.html#receivefrom><tt>receivefrom</tt></a>
can no longer be used, but the method
<a href=udp.html#send><tt>send</tt></a> can be used to send data
directly to the peer, and the method
<a href=udp.html#receive><tt>receive</tt></a>
will only return datagrams originating
from that peer. There is about 30% performance gain due to this practice.
</p>
<p>
To associate an UDP socket with a local address, an application calls the
<a href=udp.html#setsockname><tt>setsockname</tt></a>
method <em>before</em> sending any datagrams. Otherwise, the socket is
automatically bound to an ephemeral address before the first data
transmission and once bound the local address cannot be changed.
The other methods available for UDP sockets are
<a href=udp.html#getpeername><tt>getpeername</tt></a>,
<a href=udp.html#getsockname><tt>getsockname</tt></a>,
<a href=udp.html#settimeout><tt>settimeout</tt></a>,
<a href=udp.html#setoption><tt>setoption</tt></a> and
<a href=udp.html#close><tt>close</tt></a>.
</p>
<p>
Example:
</p>
<blockquote>
<p>
A simple daytime client, using LuaSocket. The program connects to a remote
server and tries to retrieve the daytime, printing the answer it got or an
error message.
</p>
<pre class=example>
-- change here to the host an port you want to contact
local host, port = "localhost", 13
-- load namespace
local socket = require("socket")
-- convert host name to ip address
local ip = assert(socket.dns.toip(host))
-- create a new UDP object
local udp = assert(socket.udp())
-- contact daytime host
assert(udp:sendto("anything", ip, port))
-- retrieve the answer and print results
io.write(assert(udp:receive()))
</pre>
</blockquote>
<!-- More +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id=more>Support modules</h3>
<p> Although not covered in the introduction, LuaSocket offers
much more than TCP and UDP functionality. As the library
evolved, support for <a href=http.html>HTTP</a>, <a href=ftp.html>FTP</a>,
and <a href=smtp.html>SMTP</a> were built on top of these. These modules
and many others are covered by the <a href=reference.html>reference manual</a>.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#down">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:36 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

430
doc/ltn12.html Normal file
View File

@ -0,0 +1,430 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: LTN12 support">
<meta name="keywords" content="Lua, LuaSocket, Filters, Source, Sink,
Pump, Support, Library">
<title>LuaSocket: LTN12 module</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- ltn12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=ltn12>LTN12</h2>
<p> The <tt>ltn12</tt> namespace implements the ideas described in
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
LTN012, Filters sources and sinks</a>. This manual simply describes the
functions. Please refer to the LTN for a deeper explanation of the
functionality provided by this module.
</p>
<p>
To obtain the <tt>ltn12</tt> namespace, run:
</p>
<pre class=example>
-- loads the LTN21 module
local ltn12 = require("ltn12")
</pre>
<!-- filters ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id="filter">Filters</h3>
<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="filter.chain">
ltn12.filter.<b>chain(</b>filter<sub>1</sub>, filter<sub>2</sub>
[, ... filter<sub>N</sub>]<b>)</b>
</p>
<p class=description>
Returns a filter that passes all data it receives through each of a
series of given filters.
</p>
<p class=parameters>
<tt>Filter<sub>1</sub></tt> to <tt>filter<sub>N</sub></tt> are simple
filters.
</p>
<p class=return>
The function returns the chained filter.
</p>
<p class=note>
The nesting of filters can be arbitrary. For instance, the useless filter
below doesn't do anything but return the data that was passed to it,
unaltered.
</p>
<pre class=example>
-- load required modules
local ltn12 = require("ltn12")
local mime = require("mime")
-- create a silly identity filter
id = ltn12.filter.chain(
mime.encode("quoted-printable"),
mime.encode("base64"),
mime.decode("base64"),
mime.decode("quoted-printable")
)
</pre>
<!-- cycle ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="filter.cycle">
ltn12.filter.<b>cycle(</b>low [, ctx, extra]<b>)</b>
</p>
<p class=description>
Returns a high-level filter that cycles though a low-level filter by
passing it each chunk and updating a context between calls.
</p>
<p class=parameters>
<tt>Low</tt> is the low-level filter to be cycled,
<tt>ctx</tt> is the initial context and <tt>extra</tt> is any extra
argument the low-level filter might take.
</p>
<p class=return>
The function returns the high-level filter.
</p>
<pre class=example>
-- load the ltn12 module
local ltn12 = require("ltn12")
-- the base64 mime filter factory
encodet['base64'] = function()
return ltn12.filter.cycle(b64, "")
end
</pre>
<!-- pumps ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id="pump">Pumps</h3>
<!-- all ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="pump.all">
ltn12.pump.<b>all(</b>source, sink<b>)</b>
</p>
<p class=description>
Pumps <em>all</em> data from a <tt>source</tt> to a <tt>sink</tt>.
</p>
<p class=return>
If successful, the function returns a value that evaluates to
<b><tt>true</tt></b>. In case
of error, the function returns a <b><tt>false</tt></b> value, followed by an error message.
</p>
<!-- step +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="pump.step">
ltn12.pump.<b>step(</b>source, sink<b>)</b>
</p>
<p class=description>
Pumps <em>one</em> chunk of data from a <tt>source</tt> to a <tt>sink</tt>.
</p>
<p class=return>
If successful, the function returns a value that evaluates to
<b><tt>true</tt></b>. In case
of error, the function returns a <b><tt>false</tt></b> value, followed by an error message.
</p>
<!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id="sink">Sinks</h3>
<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.chain">
ltn12.sink.<b>chain(</b>filter, sink<b>)</b>
</p>
<p class=description>
Creates and returns a new sink that passes data through a <tt>filter</tt> before sending it to a given <tt>sink</tt>.
</p>
<!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.error">
ltn12.sink.<b>error(</b>message<b>)</b>
</p>
<p class=description>
Creates and returns a sink that aborts transmission with the error
<tt>message</tt>.
</p>
<!-- file +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.file">
ltn12.sink.<b>file(</b>handle, message<b>)</b>
</p>
<p class=description>
Creates a sink that sends data to a file.
</p>
<p class=parameters>
<tt>Handle</tt> is a file handle. If <tt>handle</tt> is <tt><b>nil</b></tt>,
<tt>message</tt> should give the reason for failure.
</p>
<p class=return>
The function returns a sink that sends all data to the given <tt>handle</tt>
and closes the file when done, or a sink that aborts the transmission with
the error <tt>message</tt>
</p>
<p class=note>
In the following example, notice how the prototype is designed to
fit nicely with the <tt>io.open</tt> function.
</p>
<pre class=example>
-- load the ltn12 module
local ltn12 = require("ltn12")
-- copy a file
ltn12.pump.all(
ltn12.source.file(io.open("original.png")),
ltn12.sink.file(io.open("copy.png"))
)
</pre>
<!-- null +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.null">
ltn12.sink.<b>null()</b>
</p>
<p class=description>
Returns a sink that ignores all data it receives.
</p>
<!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.simplify">
ltn12.sink.<b>simplify(</b>sink<b>)</b>
</p>
<p class=description>
Creates and returns a simple sink given a fancy <tt>sink</tt>.
</p>
<!-- table ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="sink.table">
ltn12.sink.<b>table(</b>[table]<b>)</b>
</p>
<p class=description>
Creates a sink that stores all chunks in a table. The chunks can later be
efficiently concatenated into a single string.
</p>
<p class=parameters>
<tt>Table</tt> is used to hold the chunks. If
<tt><b>nil</b></tt>, the function creates its own table.
</p>
<p class=return>
The function returns the sink and the table used to store the chunks.
</p>
<pre class=example>
-- load needed modules
local http = require("socket.http")
local ltn12 = require("ltn12")
-- a simplified http.get function
function http.get(u)
local t = {}
local respt = request{
url = u,
sink = ltn12.sink.table(t)
}
return table.concat(t), respt.headers, respt.code
end
</pre>
<!-- sinks ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id="source">Sources</h3>
<!-- cat ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.cat">
ltn12.source.<b>cat(</b>source<sub>1</sub> [, source<sub>2</sub>, ...,
source<sub>N</sub>]<b>)</b>
</p>
<p class=description>
Creates a new source that produces the concatenation of the data produced
by a number of sources.
</p>
<p class=parameters>
<tt>Source<sub>1</sub></tt> to <tt>source<sub>N</sub></tt> are the original
sources.
</p>
<p class=return>
The function returns the new source.
</p>
<!-- chain ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.chain">
ltn12.source.<b>chain(</b>source, filter<b>)</b>
</p>
<p class=description>
Creates a new <tt>source</tt> that passes data through a <tt>filter</tt>
before returning it.
</p>
<p class=return>
The function returns the new source.
</p>
<!-- empty ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.empty">
ltn12.source.<b>empty()</b>
</p>
<p class=description>
Creates and returns an empty source.
</p>
<!-- error ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.error">
ltn12.source.<b>error(</b>message<b>)</b>
</p>
<p class=description>
Creates and returns a source that aborts transmission with the error
<tt>message</tt>.
</p>
<!-- file +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.file">
ltn12.source.<b>file(</b>handle, message<b>)</b>
</p>
<p class=description>
Creates a source that produces the contents of a file.
</p>
<p class=parameters>
<tt>Handle</tt> is a file handle. If <tt>handle</tt> is <tt><b>nil</b></tt>,
<tt>message</tt> should give the reason for failure.
</p>
<p class=return>
The function returns a source that reads chunks of data from
given <tt>handle</tt> and returns it to the user,
closing the file when done, or a source that aborts the transmission with
the error <tt>message</tt>
</p>
<p class=note>
In the following example, notice how the prototype is designed to
fit nicely with the <tt>io.open</tt> function.
</p>
<pre class=example>
-- load the ltn12 module
local ltn12 = require("ltn12")
-- copy a file
ltn12.pump.all(
ltn12.source.file(io.open("original.png")),
ltn12.sink.file(io.open("copy.png"))
)
</pre>
<!-- simplify +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.simplify">
ltn12.source.<b>simplify(</b>source<b>)</b>
</p>
<p class=description>
Creates and returns a simple source given a fancy <tt>source</tt>.
</p>
<!-- string +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="source.string">
ltn12.source.<b>string(</b>string<b>)</b>
</p>
<p class=description>
Creates and returns a source that produces the contents of a
<tt>string</tt>, chunk by chunk.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#down">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:41 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

BIN
doc/luasocket.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

476
doc/mime.html Normal file
View File

@ -0,0 +1,476 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: MIME support">
<meta name="keywords" content="Lua, LuaSocket, MIME, Library, Support">
<title>LuaSocket: MIME module</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=mime>MIME</h2>
<p>
The <tt>mime</tt> namespace offers filters that apply and remove common
content transfer encodings, such as Base64 and Quoted-Printable.
It also provides functions to break text into lines and change
the end-of-line convention.
MIME is described mainly in
<a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>,
<a href="http://www.ietf.org/rfc/rfc2046.txt">2046</a>,
<a href="http://www.ietf.org/rfc/rfc2047.txt">2047</a>,
<a href="http://www.ietf.org/rfc/rfc2047.txt">2048</a>, and
<a href="http://www.ietf.org/rfc/rfc2048.txt">2049</a>.
</p>
<p>
All functionality provided by the MIME module
follows the ideas presented in
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">
LTN012, Filters sources and sinks</a>.
</p>
<p>
To obtain the <tt>mime</tt> namespace, run:
</p>
<pre class=example>
-- loads the MIME module and everything it requires
local mime = require("mime")
</pre>
<!-- High-level +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id=high>High-level filters</h3>
<!-- normalize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="normalize">
mime.<b>normalize(</b>[marker]<b>)</b>
</p>
<p class=description>
Converts most common end-of-line markers to a specific given marker.
</p>
<p class=parameters>
<tt>Marker</tt> is the new marker. It defaults to CRLF, the canonic
end-of-line marker defined by the MIME standard.
</p>
<p class=return>
The function returns a filter that performs the conversion.
</p>
<p class=note>
Note: There is no perfect solution to this problem. Different end-of-line
markers are an evil that will probably plague developers forever.
This function, however, will work perfectly for text created with any of
the most common end-of-line markers, i.e. the Mac OS (CR), the Unix (LF),
or the DOS (CRLF) conventions. Even if the data has mixed end-of-line
markers, the function will still work well, although it doesn't
guarantee that the number of empty lines will be correct.
</p>
<!-- decode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="decode">
mime.<b>decode(</b>"base64"<b>)</b><br>
mime.<b>decode(</b>"quoted-printable"<b>)</b>
</p>
<p class=description>
Returns a filter that decodes data from a given transfer content
encoding.
</p>
<!-- encode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="encode">
mime.<b>encode(</b>"base64"<b>)</b><br>
mime.<b>encode(</b>"quoted-printable" [, mode]<b>)</b>
</p>
<p class=description>
Returns a filter that encodes data according to a given transfer content
encoding.
</p>
<p class=parameters>
In the Quoted-Printable case, the user can specify whether the data is
textual or binary, by passing the <tt>mode</tt> strings "<tt>text</tt>" or
"<tt>binary</tt>". <tt>Mode</tt> defaults to "<tt>text</tt>".
</p>
<p class=note>
Although both transfer content encodings specify a limit for the line
length, the encoding filters do <em>not</em> break text into lines (for
added flexibility).
Below is a filter that converts binary data to the Base64 transfer content
encoding and breaks it into lines of the correct size.
</p>
<pre class=example>
base64 = ltn12.filter.chain(
mime.encode("base64"),
mime.wrap("base64")
)
</pre>
<p class=note>
Note: Text data <em>has</em> to be converted to canonic form
<em>before</em> being encoded.
</p>
<pre class=example>
base64 = ltn12.filter.chain(
mime.normalize(),
mime.encode("base64"),
mime.wrap("base64")
)
</pre>
<!-- stuff +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="stuff">
mime.<b>stuff()</b><br>
</p>
<p class=description>
Creates and returns a filter that performs stuffing of SMTP messages.
</p>
<p class=note>
Note: The <a href=smtp.html#send><tt>smtp.send</tt></a> function
uses this filter automatically. You don't need to chain it with your
source, or apply it to your message body.
</p>
<!-- wrap +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="wrap">
mime.<b>wrap(</b>"text" [, length]<b>)</b><br>
mime.<b>wrap(</b>"base64"<b>)</b><br>
mime.<b>wrap(</b>"quoted-printable"<b>)</b>
</p>
<p class=description>
Returns a filter that breaks data into lines.
</p>
<p class=parameters>
The "<tt>text</tt>" line-wrap filter simply breaks text into lines by
inserting CRLF end-of-line markers at appropriate positions.
<tt>Length</tt> defaults 76.
The "<tt>base64</tt>" line-wrap filter works just like the default
"<tt>text</tt>" line-wrap filter with default length.
The function can also wrap "<tt>quoted-printable</tt>" lines, taking care
not to break lines in the middle of an escaped character. In that case, the
line length is fixed at 76.
</p>
<p class=note>
For example, to create an encoding filter for the Quoted-Printable transfer content encoding of text data, do the following:
</p>
<pre class=example>
qp = ltn12.filter.chain(
mime.normalize(),
mime.encode("quoted-printable"),
mime.wrap("quoted-printable")
)
</pre>
<p class=note>
Note: To break into lines with a different end-of-line convention, apply
a normalization filter after the line break filter.
</p>
<!-- Low-level ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h3 id=low>Low-level filters</h3>
<!-- b64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="b64">
A, B = mime.<b>b64(</b>C [, D]<b>)</b>
</p>
<p class=description>
Low-level filter to perform Base64 encoding.
</p>
<p class=description>
<tt>A</tt> is the encoded version of the largest prefix of
<tt>C..D</tt>
that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of
<tt>C..D</tt>, <em>before</em> encoding.
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with
the encoding of the remaining bytes of <tt>C</tt>.
</p>
<p class=note>
Note: The simplest use of this function is to encode a string into it's
Base64 transfer content encoding. Notice the extra parenthesis around the
call to <tt>mime.b64</tt>, to discard the second return value.
</p>
<pre class=example>
print((mime.b64("diego:password")))
--&gt; ZGllZ286cGFzc3dvcmQ=
</pre>
<!-- dot +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="dot">
A, n = mime.<b>dot(</b>m [, B]<b>)</b>
</p>
<p class=description>
Low-level filter to perform SMTP stuffing and enable transmission of
messages containing the sequence "CRLF.CRLF".
</p>
<p class=parameters>
<tt>A</tt> is the stuffed version of <tt>B</tt>. '<tt>n</tt>' gives the
number of characters from the sequence CRLF seen in the end of <tt>B</tt>.
'<tt>m</tt>' should tell the same, but for the previous chunk.
</p>
<p class=note>Note: The message body is defined to begin with
an implicit CRLF. Therefore, to stuff a message correctly, the
first <tt>m</tt> should have the value 2.
</p>
<pre class=example>
print((string.gsub(mime.dot(2, ".\r\nStuffing the message.\r\n.\r\n."), "\r\n", "\\n")))
--&gt; ..\nStuffing the message.\n..\n..
</pre>
<p class=note>
Note: The <a href=smtp.html#send><tt>smtp.send</tt></a> function
uses this filter automatically. You don't need to
apply it again.
</p>
<!-- eol ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="eol">
A, B = mime.<b>eol(</b>C [, D, marker]<b>)</b>
</p>
<p class=description>
Low-level filter to perform end-of-line marker translation.
For each chunk, the function needs to know if the last character of the
previous chunk could be part of an end-of-line marker or not. This is the
context the function receives besides the chunk. An updated version of
the context is returned after each new chunk.
</p>
<p class=parameters>
<tt>A</tt> is the translated version of <tt>D</tt>. <tt>C</tt> is the
ASCII value of the last character of the previous chunk, if it was a
candidate for line break, or 0 otherwise.
<tt>B</tt> is the same as <tt>C</tt>, but for the current
chunk. <tt>Marker</tt> gives the new end-of-line marker and defaults to CRLF.
</p>
<pre class=example>
-- translates the end-of-line marker to UNIX
unix = mime.eol(0, dos, "\n")
</pre>
<!-- qp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="qp">
A, B = mime.<b>qp(</b>C [, D, marker]<b>)</b>
</p>
<p class=description>
Low-level filter to perform Quoted-Printable encoding.
</p>
<p class=parameters>
<tt>A</tt> is the encoded version of the largest prefix of
<tt>C..D</tt>
that can be encoded unambiguously. <tt>B</tt> has the remaining bytes of
<tt>C..D</tt>, <em>before</em> encoding.
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is padded with
the encoding of the remaining bytes of <tt>C</tt>.
Throughout encoding, occurrences of CRLF are replaced by the
<tt>marker</tt>, which itself defaults to CRLF.
</p>
<p class=note>
Note: The simplest use of this function is to encode a string into it's
Quoted-Printable transfer content encoding.
Notice the extra parenthesis around the call to <tt>mime.qp</tt>, to discard the second return value.
</p>
<pre class=example>
print((mime.qp("maçã")))
--&gt; ma=E7=E3=
</pre>
<!-- qpwrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="qpwrp">
A, m = mime.<b>qpwrp(</b>n [, B, length]<b>)</b>
</p>
<p class=description>
Low-level filter to break Quoted-Printable text into lines.
</p>
<p class=parameters>
<tt>A</tt> is a copy of <tt>B</tt>, broken into lines of at most
<tt>length</tt> bytes (defaults to 76).
'<tt>n</tt>' should tell how many bytes are left for the first
line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes
left in the last line of <tt>A</tt>.
</p>
<p class=note>
Note: Besides breaking text into lines, this function makes sure the line
breaks don't fall in the middle of an escaped character combination. Also,
this function only breaks lines that are bigger than <tt>length</tt> bytes.
</p>
<!-- unb64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="unb64">
A, B = mime.<b>unb64(</b>C [, D]<b>)</b>
</p>
<p class=description>
Low-level filter to perform Base64 decoding.
</p>
<p class=parameters>
<tt>A</tt> is the decoded version of the largest prefix of
<tt>C..D</tt>
that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of
<tt>C..D</tt>, <em>before</em> decoding.
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is the empty string
and <tt>B</tt> returns whatever couldn't be decoded.
</p>
<p class=note>
Note: The simplest use of this function is to decode a string from it's
Base64 transfer content encoding.
Notice the extra parenthesis around the call to <tt>mime.unqp</tt>, to discard the second return value.
</p>
<pre class=example>
print((mime.unb64("ZGllZ286cGFzc3dvcmQ=")))
--&gt; diego:password
</pre>
<!-- unqp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="unqp">
A, B = mime.<b>unqp(</b>C [, D]<b>)</b>
</p>
<p class=description>
Low-level filter to remove the Quoted-Printable transfer content encoding
from data.
</p>
<p class=parameters>
<tt>A</tt> is the decoded version of the largest prefix of
<tt>C..D</tt>
that can be decoded unambiguously. <tt>B</tt> has the remaining bytes of
<tt>C..D</tt>, <em>before</em> decoding.
If <tt>D</tt> is <tt><b>nil</b></tt>, <tt>A</tt> is augmented with
the encoding of the remaining bytes of <tt>C</tt>.
</p>
<p class=note>
Note: The simplest use of this function is to decode a string from it's
Quoted-Printable transfer content encoding.
Notice the extra parenthesis around the call to <tt>mime.unqp</tt>, to discard the second return value.
</p>
<pre class=example>
print((mime.qp("ma=E7=E3=")))
--&gt; maçã
</pre>
<!-- wrp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="wrp">
A, m = mime.<b>wrp(</b>n [, B, length]<b>)</b>
</p>
<p class=description>
Low-level filter to break text into lines with CRLF marker.
Text is assumed to be in the <a href=#normalize><tt>normalize</tt></a> form.
</p>
<p class=parameters>
<tt>A</tt> is a copy of <tt>B</tt>, broken into lines of at most
<tt>length</tt> bytes (defaults to 76).
'<tt>n</tt>' should tell how many bytes are left for the first
line of <tt>B</tt> and '<tt>m</tt>' returns the number of bytes
left in the last line of <tt>A</tt>.
</p>
<p class=note>
Note: This function only breaks lines that are bigger than
<tt>length</tt> bytes. The resulting line length does not include the CRLF
marker.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#down">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:44 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

54
doc/reference.css Normal file
View File

@ -0,0 +1,54 @@
body {
margin-left: 1em;
margin-right: 1em;
font-family: "Verdana", sans-serif;
}
tt {
font-family: "Andale Mono", monospace;
}
h1, h2, h3, h4 { margin-left: 0em; }
h3 { padding-top: 1em; }
p { margin-left: 1em; }
p.name {
font-family: "Andale Mono", monospace;
padding-top: 1em;
margin-left: 0em;
}
a[href] { color: #00007f; }
blockquote { margin-left: 3em; }
pre.example {
background: #ccc;
padding: 1em;
margin-left: 1em;
font-family: "Andale Mono", monospace;
font-size: small;
}
hr {
margin-left: 0em;
background: #00007f;
border: 0px;
height: 1px;
}
ul { list-style-type: disc; }
table.index { border: 1px #00007f; }
table.index td { text-align: left; vertical-align: top; }
table.index ul { padding-top: 0em; margin-top: 0em; }
h1:first-letter,
h2:first-letter,
h2:first-letter,
h3:first-letter { color: #00007f; }
div.header, div.footer { margin-left: 0em; }

240
doc/reference.html Normal file
View File

@ -0,0 +1,240 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: Index to reference manual">
<meta name="keywords" content="Lua, LuaSocket, Index, Manual, Network, Library,
Support, Manual">
<title>LuaSocket: Index to reference manual</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- reference +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2>Reference</h2>
<blockquote>
<a href="dns.html">DNS (in socket)</a>
<blockquote>
<a href="dns.html#toip">toip</a>,
<a href="dns.html#tohostname">tohostname</a>,
<a href="dns.html#gethostname">gethostname</a>.
</blockquote>
</blockquote>
<!-- ftp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote>
<a href="ftp.html">FTP</a>
<blockquote>
<a href="ftp.html#get">get</a>,
<a href="ftp.html#put">put</a>.
</blockquote>
</blockquote>
<!-- http +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote>
<a href="http.html">HTTP</a>
<blockquote>
<a href="http.html#request">request</a>.
</blockquote>
</blockquote>
<!-- ltn12 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote>
<a href="ltn12.html">LTN12</a>
<blockquote>
<a href="ltn12.html#filter">filter</a>:
<a href="ltn12.html#filter.chain">chain</a>,
<a href="ltn12.html#filter.cycle">cycle</a>.
</blockquote>
<blockquote>
<a href="ltn12.html#pump">pump</a>:
<a href="ltn12.html#pump.all">all</a>,
<a href="ltn12.html#pump.step">step</a>.
</blockquote>
<blockquote>
<a href="ltn12.html#sink">sink</a>:
<a href="ltn12.html#sink.chain">chain</a>,
<a href="ltn12.html#sink.error">error</a>,
<a href="ltn12.html#sink.file">file</a>,
<a href="ltn12.html#sink.null">null</a>,
<a href="ltn12.html#sink.simplify">simplify</a>,
<a href="ltn12.html#sink.table">table</a>.
</blockquote>
<blockquote>
<a href="ltn12.html#source">source</a>:
<a href="ltn12.html#source.cat">cat</a>,
<a href="ltn12.html#source.chain">chain</a>,
<a href="ltn12.html#source.empty">empty</a>,
<a href="ltn12.html#source.error">error</a>,
<a href="ltn12.html#source.file">file</a>,
<a href="ltn12.html#source.simplify">simplify</a>,
<a href="ltn12.html#source.string">string</a>.
</blockquote>
</blockquote>
<!-- mime +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote>
<a href="mime.html">MIME</a>
<blockquote>
<a href="mime.html#high">high-level</a>:
<a href="mime.html#normalize">normalize</a>,
<a href="mime.html#decode">decode</a>,
<a href="mime.html#encode">encode</a>,
<a href="mime.html#stuff">stuff</a>,
<a href="mime.html#wrap">wrap</a>.
</blockquote>
<blockquote>
<a href="mime.html#low">low-level</a>:
<a href="mime.html#b64">b64</a>,
<a href="mime.html#dot">dot</a>,
<a href="mime.html#eol">eol</a>,
<a href="mime.html#qp">qp</a>,
<a href="mime.html#wrp">wrp</a>,
<a href="mime.html#qpwrp">qpwrp</a>.
<a href="mime.html#unb64">unb64</a>,
<a href="mime.html#unqp">unqp</a>,
</blockquote>
</blockquote>
<!-- smtp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote>
<a href="smtp.html">SMTP</a>
<blockquote>
<a href="smtp.html#message">message</a>,
<a href="smtp.html#send">send</a>.
</blockquote>
</blockquote>
<!-- socket +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote>
<a href="socket.html">Socket</a>
<blockquote>
<a href="socket.html#debug">_DEBUG</a>,
<a href="dns.html#dns">dns</a>,
<a href="socket.html#gettime">gettime</a>,
<a href="socket.html#newtry">newtry</a>,
<a href="socket.html#protect">protect</a>,
<a href="socket.html#select">select</a>,
<a href="socket.html#sink">sink</a>,
<a href="socket.html#skip">skip</a>,
<a href="socket.html#sleep">sleep</a>,
<a href="socket.html#setsize">_SETSIZE</a>,
<a href="socket.html#source">source</a>,
<a href="tcp.html#tcp">tcp</a>,
<a href="socket.html#try">try</a>,
<a href="udp.html#udp">udp</a>,
<a href="socket.html#version">_VERSION</a>.
</blockquote>
</blockquote>
<!-- tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote>
<a href="tcp.html">TCP (in socket)</a>
<blockquote>
<a href="tcp.html#accept">accept</a>,
<a href="tcp.html#bind">bind</a>,
<a href="tcp.html#close">close</a>,
<a href="tcp.html#connect">connect</a>,
<a href="tcp.html#getpeername">getpeername</a>,
<a href="tcp.html#getsockname">getsockname</a>,
<a href="tcp.html#getstats">getstats</a>,
<a href="tcp.html#receive">receive</a>,
<a href="tcp.html#send">send</a>,
<a href="tcp.html#setoption">setoption</a>,
<a href="tcp.html#setstats">setstats</a>,
<a href="tcp.html#settimeout">settimeout</a>,
<a href="tcp.html#shutdown">shutdown</a>.
</blockquote>
</blockquote>
<!-- udp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote>
<a href="udp.html">UDP (in socket)</a>
<blockquote>
<a href="udp.html#close">close</a>,
<a href="udp.html#getpeername">getpeername</a>,
<a href="udp.html#getsockname">getsockname</a>,
<a href="udp.html#receive">receive</a>,
<a href="udp.html#receivefrom">receivefrom</a>,
<a href="udp.html#send">send</a>,
<a href="udp.html#sendto">sendto</a>,
<a href="udp.html#setpeername">setpeername</a>,
<a href="udp.html#setsockname">setsockname</a>,
<a href="udp.html#setoption">setoption</a>,
<a href="udp.html#settimeout">settimeout</a>.
</blockquote>
</blockquote>
<!-- url ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<blockquote>
<a href="url.html">URL</a>
<blockquote>
<a href="url.html#absolute">absolute</a>,
<a href="url.html#build">build</a>,
<a href="url.html#build_path">build_path</a>,
<a href="url.html#escape">escape</a>,
<a href="url.html#parse">parse</a>,
<a href="url.html#parse_path">parse_path</a>,
<a href="url.html#unescape">unescape</a>.
</blockquote>
</blockquote>
<!-- footer ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#down">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:47 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

416
doc/smtp.html Normal file
View File

@ -0,0 +1,416 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: SMTP support">
<meta name="keywords" content="Lua, LuaSocket, SMTP, E-Mail, MIME, Multipart,
Library, Support">
<title>LuaSocket: SMTP support</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- smtp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=smtp>SMTP</h2>
<p> The <tt>smtp</tt> namespace provides functionality to send e-mail
messages. The high-level API consists of two functions: one to
define an e-mail message, and another to actually send the message.
Although almost all users will find that these functions provide more than
enough functionality, the underlying implementation allows for even more
control (if you bother to read the code).
</p>
<p>The implementation conforms to the Simple Mail Transfer Protocol,
<a href="http://www.ietf.org/rfc/rfc2821.txt">RFC 2821</a>.
Another RFC of interest is <a
href="http://www.ietf.org/rfc/rfc2822.txt">RFC 2822</a>,
which governs the Internet Message Format.
Multipart messages (those that contain attachments) are part
of the MIME standard, but described mainly
in <a href="http://www.ietf.org/rfc/rfc2046.txt">RFC 2046</a>
<p> In the description below, good understanding of <a
href="http://lua-users.org/wiki/FiltersSourcesAndSinks"> LTN012, Filters
sources and sinks</a> and the <a href=mime.html>MIME</a> module is
assumed. In fact, the SMTP module was the main reason for their
creation. </p>
<p>
To obtain the <tt>smtp</tt> namespace, run:
</p>
<pre class=example>
-- loads the SMTP module and everything it requires
local smtp = require("socket.smtp")
</pre>
<p>
MIME headers are represented as a Lua table in the form:
</p>
<blockquote>
<table summary="MIME headers in Lua table">
<tr><td><tt>
headers = {<br>
&nbsp;&nbsp;field-1-name = <i>field-1-value</i>,<br>
&nbsp;&nbsp;field-2-name = <i>field-2-value</i>,<br>
&nbsp;&nbsp;field-3-name = <i>field-3-value</i>,<br>
&nbsp;&nbsp;...<br>
&nbsp;&nbsp;field-n-name = <i>field-n-value</i><br>
}
</tt></td></tr>
</table>
</blockquote>
<p>
Field names are case insensitive (as specified by the standard) and all
functions work with lowercase field names.
Field values are left unmodified.
</p>
<p class=note>
Note: MIME headers are independent of order. Therefore, there is no problem
in representing them in a Lua table.
</p>
<p>
The following constants can be set to control the default behavior of
the SMTP module:
</p>
<ul>
<li> <tt>DOMAIN</tt>: domain used to greet the server;
<li> <tt>PORT</tt>: default port used for the connection;
<li> <tt>SERVER</tt>: default server used for the connection;
<li> <tt>TIMEOUT</tt>: default timeout for all I/O operations;
<li> <tt>ZONE</tt>: default time zone.
</ul>
<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=send>
smtp.<b>send{</b><br>
&nbsp;&nbsp;from = <i>string</i>,<br>
&nbsp;&nbsp;rcpt = <i>string</i> or <i>string-table</i>,<br>
&nbsp;&nbsp;source = <i>LTN12 source</i>,<br>
&nbsp;&nbsp;[user = <i>string</i>,]<br>
&nbsp;&nbsp;[password = <i>string</i>,]<br>
&nbsp;&nbsp;[server = <i>string</i>,]<br>
&nbsp;&nbsp;[port = <i>number</i>,]<br>
&nbsp;&nbsp;[domain = <i>string</i>,]<br>
&nbsp;&nbsp;[step = <i>LTN12 pump step</i>,]<br>
&nbsp;&nbsp;[create = <i>function</i>]<br>
<b>}</b>
</p>
<p class=description>
Sends a message to a recipient list. Since sending messages is not as
simple as downloading an URL from a FTP or HTTP server, this function
doesn't have a simple interface. However, see the
<a href=#message><tt>message</tt></a> source factory for
a very powerful way to define the message contents.
</p>
<p class=parameters>
The sender is given by the e-mail address in the <tt>from</tt> field.
<tt>Rcpt</tt> is a Lua table with one entry for each recipient e-mail
address, or a string
in case there is just one recipient.
The contents of the message are given by a <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<tt>source</tt>. Several arguments are optional:
</p>
<ul>
<li> <tt>user</tt>, <tt>password</tt>: User and password for
authentication. The function will attempt LOGIN and PLAIN authentication
methods if supported by the server (both are unsafe);
<li> <tt>server</tt>: Server to connect to. Defaults to "localhost";
<li> <tt>port</tt>: Port to connect to. Defaults to 25;
<li> <tt>domain</tt>: Domain name used to greet the server; Defaults to the
local machine host name;
<li> <tt>step</tt>:
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
pump step function used to pass data from the
source to the server. Defaults to the LTN12 <tt>pump.step</tt> function;
<li><tt>create</tt>: An optional function to be used instead of
<a href=tcp.html#socket.tcp><tt>socket.tcp</tt></a> when the communications socket is created.
</ul>
<p class=return>
If successful, the function returns 1. Otherwise, the function returns
<b><tt>nil</tt></b> followed by an error message.
</p>
<p class=note>
Note: SMTP servers can be very picky with the format of e-mail
addresses. To be safe, use only addresses of the form
"<tt>&lt;fulano@example.com&gt;</tt>" in the <tt>from</tt> and
<tt>rcpt</tt> arguments to the <tt>send</tt> function. In headers, e-mail
addresses can take whatever form you like. </p>
<p class=note>
Big note: There is a good deal of misconception with the use of the
destination address field headers, i.e., the '<tt>To</tt>', '<tt>Cc</tt>',
and, more importantly, the '<tt>Bcc</tt>' headers. Do <em>not</em> add a
'<tt>Bcc</tt>' header to your messages because it will probably do the
exact opposite of what you expect.
</p>
<p class=note>
Only recipients specified in the <tt>rcpt</tt> list will receive a copy of the
message. Each recipient of an SMTP mail message receives a copy of the
message body along with the headers, and nothing more. The headers
<em>are</em> part of the message and should be produced by the
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
<tt>source</tt> function. The <tt>rcpt</tt> list is <em>not</em>
part of the message and will not be sent to anyone.
</p>
<p class=note>
<a href="http://www.ietf.org/rfc/rfc2822.txt">RFC 2822</a>
has two <em>important and short</em> sections, "3.6.3. Destination address
fields" and "5. Security considerations", explaining the proper
use of these headers. Here is a summary of what it says:
</p>
<ul>
<li> <tt>To</tt>: contains the address(es) of the primary recipient(s)
of the message;
<li> <tt>Cc</tt>: (where the "Cc" means "Carbon Copy" in the sense of
making a copy on a typewriter using carbon paper) contains the
addresses of others who are to receive the message, though the
content of the message may not be directed at them;
<li> <tt>Bcc</tt>: (where the "Bcc" means "Blind Carbon
Copy") contains addresses of recipients of the message whose addresses are not to be revealed to other recipients of the message.
</ul>
<p class=note>
The LuaSocket <tt>send</tt> function does not care or interpret the
headers you send, but it gives you full control over what is sent and
to whom it is sent:
</p>
<ul>
<li> If someone is to receive the message, the e-mail address <em>has</em>
to be in the recipient list. This is the only parameter that controls who
gets a copy of the message;
<li> If there are multiple recipients, none of them will automatically
know that someone else got that message. That is, the default behavior is
similar to the <tt>Bcc</tt> field of popular e-mail clients;
<li> It is up to you to add the <tt>To</tt> header with the list of primary
recipients so that other recipients can see it;
<li> It is also up to you to add the <tt>Cc</tt> header with the
list of additional recipients so that everyone else sees it;
<li> Adding a header <tt>Bcc</tt> is nonsense, unless it is
empty. Otherwise, everyone receiving the message will see it and that is
exactly what you <em>don't</em> want to happen!
</ul>
<p class=note>
I hope this clarifies the issue. Otherwise, please refer to
<a href="http://www.ietf.org/rfc/rfc2821.txt">RFC 2821</a>
and
<a href="http://www.ietf.org/rfc/rfc2822.txt">RFC 2822</a>.
</p>
<pre class=example>
-- load the smtp support
local smtp = require("socket.smtp")
-- Connects to server "localhost" and sends a message to users
-- "fulano@example.com", "beltrano@example.com",
-- and "sicrano@example.com".
-- Note that "fulano" is the primary recipient, "beltrano" receives a
-- carbon copy and neither of them knows that "sicrano" received a blind
-- carbon copy of the message.
from = "&lt;luasocket@example.com&gt;"
rcpt = {
"&lt;fulano@example.com&gt;",
"&lt;beltrano@example.com&gt;",
"&lt;sicrano@example.com&gt;"
}
mesgt = {
headers = {
to = "Fulano da Silva &lt;fulano@example.com&gt;",
cc = '"Beltrano F. Nunes" &lt;beltrano@example.com&gt;',
subject = "My first message"
},
body = "I hope this works. If it does, I can send you another 1000 copies."
}
r, e = smtp.send{
from = from,
rcpt = rcpt,
source = smtp.message(mesgt)
}
</pre>
<!-- message ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=message>
smtp.<b>message(</b>mesgt<b>)</b>
</p>
<p class=description>
Returns a <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> source that sends an SMTP message body, possibly multipart (arbitrarily deep).
</p>
<p class=parameters>
The only parameter of the function is a table describing the message.
<tt>Mesgt</tt> has the following form (notice the recursive structure):
</p>
<blockquote>
<table summary="Mesgt table structure">
<tr><td><tt>
mesgt = {<br>
&nbsp;&nbsp;headers = <i>header-table</i>,<br>
&nbsp;&nbsp;body = <i>LTN12 source</i> or <i>string</i> or
<i>multipart-mesgt</i><br>
}<br>
&nbsp;<br>
multipart-mesgt = {<br>
&nbsp;&nbsp;[preamble = <i>string</i>,]<br>
&nbsp;&nbsp;[1] = <i>mesgt</i>,<br>
&nbsp;&nbsp;[2] = <i>mesgt</i>,<br>
&nbsp;&nbsp;...<br>
&nbsp;&nbsp;[<i>n</i>] = <i>mesgt</i>,<br>
&nbsp;&nbsp;[epilogue = <i>string</i>,]<br>
}<br>
</tt></td></tr>
</table>
</blockquote>
<p class=parameters>
For a simple message, all that is needed is a set of <tt>headers</tt>
and the <tt>body</tt>. The message <tt>body</tt> can be given as a string
or as a <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
source. For multipart messages, the body is a table that
recursively defines each part as an independent message, plus an optional
<tt>preamble</tt> and <tt>epilogue</tt>.
</p>
<p class=return>
The function returns a <em>simple</em>
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
source that produces the
message contents as defined by <tt>mesgt</tt>, chunk by chunk.
Hopefully, the following
example will make things clear. When in doubt, refer to the appropriate RFC
as listed in the introduction. </p>
<pre class=example>
-- load the smtp support and its friends
local smtp = require("socket.smtp")
local mime = require("mime")
local ltn12 = require("ltn12")
-- creates a source to send a message with two parts. The first part is
-- plain text, the second part is a PNG image, encoded as base64.
source = smtp.message{
headers = {
-- Remember that headers are *ignored* by smtp.send.
from = "Sicrano de Oliveira &lt;sicrano@example.com&gt;",
to = "Fulano da Silva &lt;fulano@example.com&gt;",
subject = "Here is a message with attachments"
},
body = {
preamble = "If your client doesn't understand attachments, \r\n" ..
"it will still display the preamble and the epilogue.\r\n" ..
"Preamble will probably appear even in a MIME enabled client.",
-- first part: no headers means plain text, us-ascii.
-- The mime.eol low-level filter normalizes end-of-line markers.
[1] = {
body = mime.eol(0, [[
Lines in a message body should always end with CRLF.
The smtp module will *NOT* perform translation. However, the
send function *DOES* perform SMTP stuffing, whereas the message
function does *NOT*.
]])
},
-- second part: headers describe content to be a png image,
-- sent under the base64 transfer content encoding.
-- notice that nothing happens until the message is actually sent.
-- small chunks are loaded into memory right before transmission and
-- translation happens on the fly.
[2] = {
headers = {
["content-type"] = 'image/png; name="image.png"',
["content-disposition"] = 'attachment; filename="image.png"',
["content-description"] = 'a beautiful image',
["content-transfer-encoding"] = "BASE64"
},
body = ltn12.source.chain(
ltn12.source.file(io.open("image.png", "rb")),
ltn12.filter.chain(
mime.encode("base64"),
mime.wrap()
)
)
},
epilogue = "This might also show up, but after the attachments"
}
}
-- finally send it
r, e = smtp.send{
from = "&lt;sicrano@example.com&gt;",
rcpt = "&lt;fulano@example.com&gt;",
source = source,
}
</pre>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#down">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:51 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

425
doc/socket.html Normal file
View File

@ -0,0 +1,425 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: The core namespace">
<meta name="keywords" content="Lua, LuaSocket, Socket, Network, Library, Support">
<title>LuaSocket: The socket namespace</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- socket +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=socket>The socket namespace</h2>
<p>
The <tt>socket</tt> namespace contains the core functionality of LuaSocket.
</p>
<p>
To obtain the <tt>socket</tt> namespace, run:
</p>
<pre class=example>
-- loads the socket module
local socket = require("socket")
</pre>
<!-- bind ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=bind>
socket.<b>bind(</b>address, port [, backlog]<b>)</b>
</p>
<p class=description>
This function is a shortcut that creates and returns a TCP server object
bound to a local <tt>address</tt> and <tt>port</tt>, ready to
accept client connections. Optionally,
user can also specify the <tt>backlog</tt> argument to the
<a href=tcp.html#listen><tt>listen</tt></a> method (defaults to 32).
</p>
<p class=note>
Note: The server object returned will have the option "<tt>reuseaddr</tt>"
set to <tt><b>true</b></tt>.
</p>
<!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=connect>
socket.<b>connect(</b>address, port [, locaddr, locport]<b>)</b>
</p>
<p class=description>
This function is a shortcut that creates and returns a TCP client object
connected to a remote <tt>host</tt> at a given <tt>port</tt>. Optionally,
the user can also specify the local address and port to bind
(<tt>locaddr</tt> and <tt>locport</tt>).
</p>
<!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=debug>
socket.<b>_DEBUG</b>
</p>
<p class=description>
This constant is set to <tt><b>true</b></tt> if the library was compiled
with debug support.
</p>
<!-- get time +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=gettime>
socket.<b>gettime()</b>
</p>
<p class=description>
Returns the time in seconds, relative to the origin of the
universe. You should subtract the values returned by this function
to get meaningful values.
</p>
<pre class=example>
t = socket.gettime()
-- do stuff
print(socket.gettime() - t .. " seconds elapsed")
</pre>
<!-- newtry +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=newtry>
socket.<b>newtry(</b>finalizer<b>)</b>
</p>
<p class=description>
Creates and returns a <em>clean</em>
<a href="#try"><tt>try</tt></a>
function that allows for cleanup before the exception
is raised.
</p>
<p class=parameters>
<tt>Finalizer</tt> is a function that will be called before
<tt>try</tt> throws the exception. It will be called
in <em>protected</em> mode.
</p>
<p class=return>
The function returns your customized <tt>try</tt> function.
</p>
<p class=note>
Note: This idea saved a <em>lot</em> of work with the
implementation of protocols in LuaSocket:
</p>
<pre class=example>
foo = socket.protect(function()
-- connect somewhere
local c = socket.try(socket.connect("somewhere", 42))
-- create a try function that closes 'c' on error
local try = socket.newtry(function() c:close() end)
-- do everything reassured c will be closed
try(c:send("hello there?\r\n"))
local answer = try(c:receive())
...
try(c:send("good bye\r\n"))
c:close()
end)
</pre>
<!-- protect +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=protect>
socket.<b>protect(</b>func<b>)</b>
</p>
<p class=description>
Converts a function that throws exceptions into a safe function. This
function only catches exceptions thrown by the <a href=#try><tt>try</tt></a>
and <a href=#newtry><tt>newtry</tt></a> functions. It does not catch normal
Lua errors.
</p>
<p class=parameters>
<tt>Func</tt> is a function that calls
<a href=#try><tt>try</tt></a> (or <tt>assert</tt>, or <tt>error</tt>)
to throw exceptions.
</p>
<p class=return>
Returns an equivalent function that instead of throwing exceptions,
returns <tt><b>nil</b></tt> followed by an error message.
</p>
<p class=note>
Note: Beware that if your function performs some illegal operation that
raises an error, the protected function will catch the error and return it
as a string. This is because the <a href=#try><tt>try</tt></a> function
uses errors as the mechanism to throw exceptions.
</p>
<!-- select +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=select>
socket.<b>select(</b>recvt, sendt [, timeout]<b>)</b>
</p>
<p class=description>
Waits for a number of sockets to change status.
</p>
<p class=parameters>
<tt>Recvt</tt> is an array with the sockets to test for characters
available for reading. Sockets in the <tt>sendt</tt> array are watched to
see if it is OK to immediately write on them. <tt>Timeout</tt> is the
maximum amount of time (in seconds) to wait for a change in status. A
<tt><b>nil</b></tt>, negative or omitted <tt>timeout</tt> value allows the
function to block indefinitely. <tt>Recvt</tt> and <tt>sendt</tt> can also
be empty tables or <tt><b>nil</b></tt>. Non-socket values (or values with
non-numeric indices) in the arrays will be silently ignored.
</p>
<p class=return> The function returns a list with the sockets ready for
reading, a list with the sockets ready for writing and an error message.
The error message is "<tt>timeout</tt>" if a timeout condition was met and
<tt><b>nil</b></tt> otherwise. The returned tables are
doubly keyed both by integers and also by the sockets
themselves, to simplify the test if a specific socket has
changed status.
</p>
<p class=note>
<b>Note: </b>: <tt>select</tt> can monitor a limited number
of sockets, as defined by the constant <tt>socket._SETSIZE</tt>. This
number may be as high as 1024 or as low as 64 by default,
depending on the system. It is usually possible to change this
at compile time. Invoking <tt>select</tt> with a larger
number of sockets will raise an error.
</p>
<p class=note>
<b>Important note</b>: a known bug in WinSock causes <tt>select</tt> to fail
on non-blocking TCP sockets. The function may return a socket as
writable even though the socket is <em>not</em> ready for sending.
</p>
<p class=note>
<b>Another important note</b>: calling select with a server socket in the receive parameter before a call to accept does <em>not</em> guarantee
<a href=tcp.html#accept><tt>accept</tt></a> will return immediately.
Use the <a href=tcp.html#settimeout><tt>settimeout</tt></a>
method or <tt>accept</tt> might block forever.
</p>
<p class=note>
<b>Yet another note</b>: If you close a socket and pass
it to <tt>select</tt>, it will be ignored.
</p>
<!-- sink ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=sink>
socket.<b>sink(</b>mode, socket<b>)</b>
</p>
<p class=description>
Creates an
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
sink from a stream socket object.
</p>
<p class=parameters>
<tt>Mode</tt> defines the behavior of the sink. The following
options are available:
</p>
<ul>
<li> <tt>"http-chunked"</tt>: sends data through socket after applying the
<em>chunked transfer coding</em>, closing the socket when done;
<li> <tt>"close-when-done"</tt>: sends all received data through the
socket, closing the socket when done;
<li> <tt>"keep-open"</tt>: sends all received data through the
socket, leaving it open when done.
</ul>
<p>
<tt>Socket</tt> is the stream socket object used to send the data.
</p>
<p class=return>
The function returns a sink with the appropriate behavior.
</p>
<!-- skip ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=skip>
socket.<b>skip(</b>d [, ret<sub>1</sub>, ret<sub>2</sub> ... ret<sub>N</sub>]<b>)</b>
</p>
<p class=description>
Drops a number of arguments and returns the remaining.
</p>
<p class=parameters>
<tt>D</tt> is the number of arguments to drop. <tt>Ret<sub>1</sub></tt> to
<tt>ret<sub>N</sub></tt> are the arguments.
</p>
<p class=return>
The function returns <tt>ret<sub>d+1</sub></tt> to <tt>ret<sub>N</sub></tt>.
</p>
<p class=note>
Note: This function is useful to avoid creation of dummy variables:
</p>
<pre class=example>
-- get the status code and separator from SMTP server reply
local code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
</pre>
<!-- sleep ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=sleep>
socket.<b>sleep(</b>time<b>)</b>
</p>
<p class=description>
Freezes the program execution during a given amount of time.
</p>
<p class=parameters>
<tt>Time</tt> is the number of seconds to sleep for. If
<tt>time</tt> is negative, the function returns immediately.
</p>
<!-- source +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=source>
socket.<b>source(</b>mode, socket [, length]<b>)</b>
</p>
<p class=description>
Creates an
<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a>
source from a stream socket object.
</p>
<p class=parameters>
<tt>Mode</tt> defines the behavior of the source. The following
options are available:
</p>
<ul>
<li> <tt>"http-chunked"</tt>: receives data from socket and removes the
<em>chunked transfer coding</em> before returning the data;
<li> <tt>"by-length"</tt>: receives a fixed number of bytes from the
socket. This mode requires the extra argument <tt>length</tt>;
<li> <tt>"until-closed"</tt>: receives data from a socket until the other
side closes the connection.
</ul>
<p>
<tt>Socket</tt> is the stream socket object used to receive the data.
</p>
<p class=return>
The function returns a source with the appropriate behavior.
</p>
<!-- setsize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=setsize>
socket.<b>_SETSIZE</b>
</p>
<p class=description>
The maximum number of sockets that the <a
href=#select><tt>select</tt></a> function can handle.
</p>
<!-- try ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=try>
socket.<b>try(</b>ret<sub>1</sub> [, ret<sub>2</sub> ... ret<sub>N</sub>]<b>)</b>
</p>
<p class=description>
Throws an exception in case of error. The exception can only be caught
by the <a href=#protect><tt>protect</tt></a> function. It does not explode
into an error message.
</p>
<p class=parameters>
<tt>Ret<sub>1</sub></tt> to <tt>ret<sub>N</sub></tt> can be arbitrary
arguments, but are usually the return values of a function call
nested with <tt>try</tt>.
</p>
<p class=return>
The function returns <tt>ret</tt><sub>1</sub> to <tt>ret</tt><sub>N</sub> if
<tt>ret</tt><sub>1</sub> is not <tt><b>nil</b></tt>. Otherwise, it calls <tt>error</tt> passing <tt>ret</tt><sub>2</sub>.
</p>
<pre class=example>
-- connects or throws an exception with the appropriate error message
c = socket.try(socket.connect("localhost", 80))
</pre>
<!-- version ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=version>
socket.<b>_VERSION</b>
</p>
<p class=description>
This constant has a string describing the current LuaSocket version.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#down">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:54 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

533
doc/tcp.html Normal file
View File

@ -0,0 +1,533 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: The TCP/IP support">
<meta name="keywords" content="Lua, LuaSocket, Socket, TCP, Library, Network, Support">
<title>LuaSocket: TCP/IP support</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- tcp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=tcp>TCP</h2>
<!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=socket.tcp>
socket.<b>tcp()</b>
</p>
<p class=description>
Creates and returns a TCP master object. A master object can
be transformed into a server object with the method
<a href=#listen><tt>listen</tt></a> (after a call to <a
href=#bind><tt>bind</tt></a>) or into a client object with
the method <a href=#connect><tt>connect</tt></a>. The only other
method supported by a master object is the
<a href=#close><tt>close</tt></a> method.</p>
<p class=return>
In case of success, a new master object is returned. In case of error,
<b><tt>nil</tt></b> is returned, followed by an error message.
</p>
<!-- accept +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=accept>
server:<b>accept()</b>
</p>
<p class=description>
Waits for a remote connection on the server
object and returns a client object representing that connection.
</p>
<p class=return>
If a connection is successfully initiated, a client object is returned.
If a timeout condition is met, the method returns <b><tt>nil</tt></b>
followed by the error string '<tt>timeout</tt>'. Other errors are
reported by <b><tt>nil</tt></b> followed by a message describing the error.
</p>
<p class=note>
Note: calling <a href=socket.html#select><tt>socket.select</tt></a>
with a server object in
the <tt>recvt</tt> parameter before a call to <tt>accept</tt> does
<em>not</em> guarantee <tt>accept</tt> will return immediately. Use the <a
href=#settimeout><tt>settimeout</tt></a> method or <tt>accept</tt>
might block until <em>another</em> client shows up.
</p>
<!-- bind +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=bind>
master:<b>bind(</b>address, port<b>)</b>
</p>
<p class=description>
Binds a master object to <tt>address</tt> and <tt>port</tt> on the
local host.
<p class=parameters>
<tt>Address</tt> can be an IP address or a host name.
<tt>Port</tt> must be an integer number in the range [0..64K).
If <tt>address</tt>
is '<tt>*</tt>', the system binds to all local interfaces
using the <tt>INADDR_ANY</tt> constant. If <tt>port</tt> is 0, the system automatically
chooses an ephemeral port.
</p>
<p class=return>
In case of success, the method returns 1. In case of error, the
method returns <b><tt>nil</tt></b> followed by an error message.
</p>
<p class=note>
Note: The function <a href=socket.html#bind><tt>socket.bind</tt></a>
is available and is a shortcut for the creation of server sockets.
</p>
<!-- close ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=close>
master:<b>close()</b><br>
client:<b>close()</b><br>
server:<b>close()</b>
</p>
<p class=description>
Closes a TCP object. The internal socket used by the object is closed
and the local address to which the object was
bound is made available to other applications. No further operations
(except for further calls to the <tt>close</tt> method) are allowed on
a closed socket.
</p>
<p class=note>
Note: It is important to close all used sockets once they are not
needed, since, in many systems, each socket uses a file descriptor,
which are limited system resources. Garbage-collected objects are
automatically closed before destruction, though.
</p>
<!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=connect>
master:<b>connect(</b>address, port<b>)</b>
</p>
<p class=description>
Attempts to connect a master object to a remote host, transforming it into a
client object.
Client objects support methods
<a href=#send><tt>send</tt></a>,
<a href=#receive><tt>receive</tt></a>,
<a href=#getsockname><tt>getsockname</tt></a>,
<a href=#getpeername><tt>getpeername</tt></a>,
<a href=#settimeout><tt>settimeout</tt></a>,
and <a href=#close><tt>close</tt></a>.
</p>
<p class=parameters>
<tt>Address</tt> can be an IP address or a host name.
<tt>Port</tt> must be an integer number in the range [1..64K).
</p>
<p class=return>
In case of error, the method returns <b><tt>nil</tt></b> followed by a string
describing the error. In case of success, the method returns 1.
</p>
<p class=note>
Note: The function <a href=socket.html#connect><tt>socket.connect</tt></a>
is available and is a shortcut for the creation of client sockets.
</p>
<p class=note>
Note: Starting with LuaSocket 2.0,
the <a href=#settimeout><tt>settimeout</tt></a>
method affects the behavior of <tt>connect</tt>, causing it to return
with an error in case of a timeout. If that happens, you can still call <a
href=socket.html#select><tt>socket.select</tt></a> with the socket in the
<tt>sendt</tt> table. The socket will be writable when the connection is
established.
</p>
<!-- getpeername ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=getpeername>
client:<b>getpeername()</b>
</p>
<p class=description>
Returns information about the remote side of a connected client object.
</p>
<p class=return>
Returns a string with the IP address of the peer, followed by the
port number that peer is using for the connection.
In case of error, the method returns <b><tt>nil</tt></b>.
</p>
<p class=note>
Note: It makes no sense to call this method on server objects.
</p>
<!-- getsockname ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=getsockname>
master:<b>getsockname()</b><br>
client:<b>getsockname()</b><br>
server:<b>getsockname()</b>
</p>
<p class=description>
Returns the local address information associated to the object.
</p>
<p class=return>
The method returns a string with local IP address and a number with
the port. In case of error, the method returns <b><tt>nil</tt></b>.
</p>
<!-- getstats +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=getstats>
master:<b>getstats()</b><br>
client:<b>getstats()</b><br>
server:<b>getstats()</b><br>
</p>
<p class=description>
Returns accounting information on the socket, useful for throttling
of bandwidth.
</p>
<p class=return>
The method returns the number of bytes received, the number of bytes sent,
and the age of the socket object in seconds.
</p>
<!-- listen ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=listen>
master:<b>listen(</b>backlog<b>)</b>
</p>
<p class=description>
Specifies the socket is willing to receive connections, transforming the
object into a server object. Server objects support the
<a href=#accept><tt>accept</tt></a>,
<a href=#getsockname><tt>getsockname</tt></a>,
<a href=#setoption><tt>setoption</tt></a>,
<a href=#settimeout><tt>settimeout</tt></a>,
and <a href=#close><tt>close</tt></a> methods.
</p>
<p class=parameters>
The parameter <tt>backlog</tt> specifies the number of client
connections that can
be queued waiting for service. If the queue is full and another client
attempts connection, the connection is refused.
</p>
<p class=return>
In case of success, the method returns 1. In case of error, the
method returns <b><tt>nil</tt></b> followed by an error message.
</p>
<!-- receive ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=receive>
client:<b>receive(</b>[pattern [, prefix]]<b>)</b>
</p>
<p class=description>
Reads data from a client object, according to the specified <em>read
pattern</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible.
</p>
<p class=parameters>
<tt>Pattern</tt> can be any of the following:
</p>
<ul>
<li> '<tt>*a</tt>': reads from the socket until the connection is
closed. No end-of-line translation is performed;
<li> '<tt>*l</tt>': reads a line of text from the socket. The line is
terminated by a LF character (ASCII&nbsp;10), optionally preceded by a
CR character (ASCII&nbsp;13). The CR and LF characters are not included in
the returned line. In fact, <em>all</em> CR characters are
ignored by the pattern. This is the default pattern;
<li> <tt>number</tt>: causes the method to read a specified <tt>number</tt>
of bytes from the socket.
</ul>
<p class=parameters>
<tt>Prefix</tt> is an optional string to be concatenated to the beginning
of any received data before return.
</p>
<p class=return>
If successful, the method returns the received pattern. In case of error,
the method returns <tt><b>nil</b></tt> followed by an error
message, followed by a (possibly empty) string containing
the partial that was received. The error message can be
the string '<tt>closed</tt>' in case the connection was
closed before the transmission was completed or the string
'<tt>timeout</tt>' in case there was a timeout during the operation.
</p>
<p class=note>
<b>Important note</b>: This function was changed <em>severely</em>. It used
to support multiple patterns (but I have never seen this feature used) and
now it doesn't anymore. Partial results used to be returned in the same
way as successful results. This last feature violated the idea that all
functions should return <tt><b>nil</b></tt> on error. Thus it was changed
too.
</p>
<!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=send>
client:<b>send(</b>data [, i [, j]]<b>)</b>
</p>
<p class=description>
Sends <tt>data</tt> through client object.
</p>
<p class=parameters>
<tt>Data</tt> is the string to be sent. The optional arguments
<tt>i</tt> and <tt>j</tt> work exactly like the standard
<tt>string.sub</tt> Lua function to allow the selection of a
substring to be sent.
</p>
<p class=return>
If successful, the method returns the index of the last byte
within <tt>[i, j]</tt> that has been sent. Notice that, if
<tt>i</tt> is 1 or absent, this is effectively the total
number of bytes sent. In case of error, the method returns
<b><tt>nil</tt></b>, followed by an error message, followed
by the index of the last byte within <tt>[i, j]</tt> that
has been sent. You might want to try again from the byte
following that. The error message can be '<tt>closed</tt>'
in case the connection was closed before the transmission
was completed or the string '<tt>timeout</tt>' in case
there was a timeout during the operation.
</p>
<p class=note>
Note: Output is <em>not</em> buffered. For small strings,
it is always better to concatenate them in Lua
(with the '<tt>..</tt>' operator) and send the result in one call
instead of calling the method several times.
</p>
<!-- setoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=setoption>
client:<b>setoption(</b>option [, value]<b>)</b><br>
server:<b>setoption(</b>option [, value]<b>)</b>
</p>
<p class=description>
Sets options for the TCP object. Options are only needed by low-level or
time-critical applications. You should only modify an option if you
are sure you need it.
</p>
<p class=parameters>
<tt>Option</tt> is a string with the option name, and <tt>value</tt>
depends on the option being set:
<ul>
<li> '<tt>keepalive</tt>': Setting this option to <tt>true</tt> enables
the periodic transmission of messages on a connected socket. Should the
connected party fail to respond to these messages, the connection is
considered broken and processes using the socket are notified;
<li> '<tt>linger</tt>': Controls the action taken when unsent data are
queued on a socket and a close is performed. The value is a table with a
boolean entry '<tt>on</tt>' and a numeric entry for the time interval
'<tt>timeout</tt>' in seconds. If the '<tt>on</tt>' field is set to
<tt>true</tt>, the system will block the process on the close attempt until
it is able to transmit the data or until '<tt>timeout</tt>' has passed. If
'<tt>on</tt>' is <tt>false</tt> and a close is issued, the system will
process the close in a manner that allows the process to continue as
quickly as possible. I do not advise you to set this to anything other than
zero;
<li> '<tt>reuseaddr</tt>': Setting this option indicates that the rules
used in validating addresses supplied in a call to
<a href=#bind><tt>bind</tt></a> should allow reuse of local addresses;
<li> '<tt>tcp-nodelay</tt>': Setting this option to <tt>true</tt>
disables the Nagle's algorithm for the connection.
</ul>
<p class=return>
The method returns 1 in case of success, or <b><tt>nil</tt></b> otherwise.
</p>
<p class=note>
Note: The descriptions above come from the man pages.
</p>
<!-- setstats +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=setstats>
master:<b>setstats(</b>received, sent, age<b>)</b><br>
client:<b>setstats(</b>received, sent, age<b>)</b><br>
server:<b>setstats(</b>received, sent, age<b>)</b><br>
</p>
<p class=description>
Resets accounting information on the socket, useful for throttling
of bandwidth.
</p>
<p class=parameters>
<tt>Received</tt> is a number with the new number of bytes received.
<tt>Sent</tt> is a number with the new number of bytes sent.
<tt>Age</tt> is the new age in seconds.
</p>
<p class=return>
The method returns 1 in case of success and <tt><b>nil</b></tt> otherwise.
</p>
<!-- settimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=settimeout>
master:<b>settimeout(</b>value [, mode]<b>)</b><br>
client:<b>settimeout(</b>value [, mode]<b>)</b><br>
server:<b>settimeout(</b>value [, mode]<b>)</b>
</p>
<p class=description>
Changes the timeout values for the object. By default,
all I/O operations are blocking. That is, any call to the methods
<a href=#send><tt>send</tt></a>,
<a href=#receive><tt>receive</tt></a>, and
<a href=#accept><tt>accept</tt></a>
will block indefinitely, until the operation completes. The
<tt>settimeout</tt> method defines a limit on the amount of time the
I/O methods can block. When a timeout is set and the specified amount of
time has elapsed, the affected methods give up and fail with an error code.
</p>
<p class=parameters>
The amount of time to wait is specified as the
<tt>value</tt> parameter, in seconds. There are two timeout modes and
both can be used together for fine tuning:
</p>
<ul>
<li> '<tt>b</tt>': <em>block</em> timeout. Specifies the upper limit on
the amount of time LuaSocket can be blocked by the operating system
while waiting for completion of any single I/O operation. This is the
default mode;</li>
<li> '<tt>t</tt>': <em>total</em> timeout. Specifies the upper limit on
the amount of time LuaSocket can block a Lua script before returning from
a call.</li>
</ul>
<p class=parameters>
The <b><tt>nil</tt></b> timeout <tt>value</tt> allows operations to block
indefinitely. Negative timeout values have the same effect.
</p>
<p class=note>
Note: although timeout values have millisecond precision in LuaSocket,
large blocks can cause I/O functions not to respect timeout values due
to the time the library takes to transfer blocks to and from the OS
and to and from the Lua interpreter. Also, function that accept host names
and perform automatic name resolution might be blocked by the resolver for
longer than the specified timeout value.
</p>
<p class=note>
Note: The old <tt>timeout</tt> method is deprecated. The name has been
changed for sake of uniformity, since all other method names already
contained verbs making their imperative nature obvious.
</p>
<!-- shutdown +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=shutdown>
client:<b>shutdown(</b>mode<b>)</b><br>
</p>
<p class=description>
Shuts down part of a full-duplex connection.
</p>
<p class=parameters>
Mode tells which way of the connection should be shut down and can
take the value:
<ul>
<li>"<tt>both</tt>": disallow further sends and receives on the object.
This is the default mode;
<li>"<tt>send</tt>": disallow further sends on the object;
<li>"<tt>receive</tt>": disallow further receives on the object.
</ul>
<p class=return>
This function returns 1.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#down">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:25:57 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

416
doc/udp.html Normal file
View File

@ -0,0 +1,416 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: The UDP support">
<meta name="keywords" content="Lua, LuaSocket, Socket, UDP, Library, Network, Support">
<title>LuaSocket: UDP support</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=udp>UDP</h2>
<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="socket.udp">
socket.<b>udp()</b>
</p>
<p class="description">
Creates and returns an unconnected UDP object. Unconnected objects support the
<a href="#sendto"><tt>sendto</tt></a>,
<a href="#receive"><tt>receive</tt></a>,
<a href="#receivefrom"><tt>receivefrom</tt></a>,
<a href="#getsockname"><tt>getsockname</tt></a>,
<a href="#setoption"><tt>setoption</tt></a>,
<a href="#settimeout"><tt>settimeout</tt></a>,
<a href="#setpeername"><tt>setpeername</tt></a>,
<a href="#setsockname"><tt>setsockname</tt></a>, and
<a href="#close"><tt>close</tt></a>.
The <a href="#setpeername"><tt>setpeername</tt></a>
is used to connect the object.
</p>
<p class="return">
In case of success, a new unconnected UDP object
returned. In case of error, <b><tt>nil</tt></b> is returned, followed by
an error message.
</p>
<!-- close +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="close">
connected:<b>close()</b><br>
unconnected:<b>close()</b>
</p>
<p class="description">
Closes a UDP object. The internal socket
used by the object is closed and the local address to which the
object was bound is made available to other applications. No
further operations (except for further calls to the <tt>close</tt>
method) are allowed on a closed socket.
</p>
<p class="note">
Note: It is important to close all used sockets
once they are not needed, since, in many systems, each socket uses
a file descriptor, which are limited system resources.
Garbage-collected objects are automatically closed before
destruction, though.
</p>
<!-- getpeername +++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="getpeername">
connected:<b>getpeername()</b>
</p>
<p class="description">
Retrieves information about the peer
associated with a connected UDP object.
</p>
<p class="return">
Returns the IP address and port number of the peer.
</p>
<p class="note">
Note: It makes no sense to call this method on unconnected objects.
</p>
<!-- getsockname +++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="getsockname">
connected:<b>getsockname()</b><br>
unconnected:<b>getsockname()</b>
</p>
<p class="description">
Returns the local address information associated to the object.
</p>
<p class="return">
The method returns a string with local IP
address and a number with the port. In case of error, the method
returns <b><tt>nil</tt></b>.
</p>
<p class="note">
Note: UDP sockets are not bound to any address
until the <a href="#setsockname"><tt>setsockname</tt></a> or the
<a href="#sendto"><tt>sendto</tt></a> method is called for the
first time (in which case it is bound to an ephemeral port and the
wild-card address).
</p>
<!-- receive +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="receive">
connected:<b>receive(</b>[size]<b>)</b><br>
unconnected:<b>receive(</b>[size]<b>)</b>
</p>
<p class="description">
Receives a datagram from the UDP object. If
the UDP object is connected, only datagrams coming from the peer
are accepted. Otherwise, the returned datagram can come from any
host.
</p>
<p class="parameters">
The optional <tt>size</tt> parameter
specifies the maximum size of the datagram to be retrieved. If
there are more than <tt>size</tt> bytes available in the datagram,
the excess bytes are discarded. If there are less then
<tt>size</tt> bytes available in the current datagram, the
available bytes are returned. If <tt>size</tt> is omitted, the
maximum datagram size is used (which is currently limited by the
implementation to 8192 bytes).
</p>
<p class="return">
In case of success, the method returns the
received datagram. In case of timeout, the method returns
<b><tt>nil</tt></b> followed by the string '<tt>timeout</tt>'.
</p>
<!-- receivefrom +++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="receivefrom">
unconnected:<b>receivefrom(</b>[size]<b>)</b>
</p>
<p class="description">
Works exactly as the <a href="#receive"><tt>receive</tt></a>
method, except it returns the IP
address and port as extra return values (and is therefore slightly less
efficient).
</p>
<!-- send ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="send">
connected:<b>send(</b>datagram<b>)</b>
</p>
<p class="description">
Sends a datagram to the UDP peer of a connected object.
</p>
<p class="parameters">
<tt>Datagram</tt> is a string with the datagram contents.
The maximum datagram size for UDP is 64K minus IP layer overhead.
However datagrams larger than the link layer packet size will be
fragmented, which may deteriorate performance and/or reliability.
</p>
<p class="return">
If successful, the method returns 1. In case of
error, the method returns <b><tt>nil</tt></b> followed by an error message.
</p>
<p class="note">
Note: In UDP, the <tt>send</tt> method never blocks
and the only way it can fail is if the underlying transport layer
refuses to send a message to the specified address (i.e. no
interface accepts the address).
</p>
<!-- sendto ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="sendto">
unconnected:<b>sendto(</b>datagram, ip, port<b>)</b>
</p>
<p class="description">
Sends a datagram to the specified IP address and port number.
</p>
<p class="parameters">
<tt>Datagram</tt> is a string with the
datagram contents.
The maximum datagram size for UDP is 64K minus IP layer overhead.
However datagrams larger than the link layer packet size will be
fragmented, which may deteriorate performance and/or reliability.
<tt>Ip</tt> is the IP address of the recipient.
Host names are <em>not</em> allowed for performance reasons.
<tt>Port</tt> is the port number at the recipient.
</p>
<p class="return">
If successful, the method returns 1. In case of
error, the method returns <b><tt>nil</tt></b> followed by an error message.
</p>
<p class="note">
Note: In UDP, the <tt>send</tt> method never blocks
and the only way it can fail is if the underlying transport layer
refuses to send a message to the specified address (i.e. no
interface accepts the address).
</p>
<!-- setpeername +++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="setpeername">
connected:<b>setpeername(</b>'*'<b>)</b><br>
unconnected:<b>setpeername(</b>address, port<b>)</b>
</p>
<p class="description">
Changes the peer of a UDP object. This
method turns an unconnected UDP object into a connected UDP
object or vice versa.
</p>
<p class="description">
For connected objects, outgoing datagrams
will be sent to the specified peer, and datagrams received from
other peers will be discarded by the OS. Connected UDP objects must
use the <a href="#send"><tt>send</tt></a> and
<a href="#receive"><tt>receive</tt></a> methods instead of
<a href="#sendto"><tt>sendto</tt></a> and
<a href="#receivefrom"><tt>receivefrom</tt></a>.
</p>
<p class="parameters">
<tt>Address</tt> can be an IP address or a
host name. <tt>Port</tt> is the port number. If <tt>address</tt> is
'<tt>*</tt>' and the object is connected, the peer association is
removed and the object becomes an unconnected object again. In that
case, the <tt>port</tt> argument is ignored.
</p>
<p class="return">
In case of error the method returns
<b><tt>nil</tt></b> followed by an error message. In case of success, the
method returns 1.
</p>
<p class="note">
Note: Since the address of the peer does not have
to be passed to and from the OS, the use of connected UDP objects
is recommended when the same peer is used for several transmissions
and can result in up to 30% performance gains.
</p>
<!-- setsockname +++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="setsockname">
unconnected:<b>setsockname(</b>address, port<b>)</b>
</p>
<p class="description">
Binds the UDP object to a local address.
</p>
<p class="parameters">
<tt>Address</tt> can be an IP address or a
host name. If <tt>address</tt> is '<tt>*</tt>' the system binds to
all local interfaces using the constant <tt>INADDR_ANY</tt>. If
<tt>port</tt> is 0, the system chooses an ephemeral port.
</p>
<p class="return">
If successful, the method returns 1. In case of
error, the method returns <b><tt>nil</tt></b> followed by an error
message.
</p>
<p class="note">
Note: This method can only be called before any
datagram is sent through the UDP object, and only once. Otherwise,
the system automatically binds the object to all local interfaces
and chooses an ephemeral port as soon as the first datagram is
sent. After the local address is set, either automatically by the
system or explicitly by <tt>setsockname</tt>, it cannot be
changed.
</p>
<!-- setoption +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="setoption">
connected:<b>setoption(</b>option [, value]<b>)</b><br>
unconnected:<b>setoption(</b>option [, value]<b>)</b>
</p>
<p class="description">
Sets options for the UDP object. Options are
only needed by low-level or time-critical applications. You should
only modify an option if you are sure you need it.</p>
<p class="parameters"><tt>Option</tt> is a string with the option
name, and <tt>value</tt> depends on the option being set:
</p>
<ul>
<li>'<tt>dontroute</tt>': Setting this option to <tt>true</tt>
indicates that outgoing messages should bypass the standard routing
facilities;</li>
<li>'<tt>broadcast</tt>': Setting this option to <tt>true</tt>
requests permission to send broadcast datagrams on the
socket.</li>
</ul>
<p class="return">
The method returns 1 in case of success, or
<b><tt>nil</tt></b> followed by an error message otherwise.
</p>
<p class="note">
Note: The descriptions above come from the man
pages.
</p>
<!-- settimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class="name" id="settimeout">
connected:<b>settimeout(</b>value<b>)</b><br>
unconnected:<b>settimeout(</b>value<b>)</b>
</p>
<p class="description">
Changes the timeout values for the object. By default, the
<a href="#receive"><tt>receive</tt></a> and
<a href="#receivefrom"><tt>receivefrom</tt></a>
operations are blocking. That is, any call to the methods will block
indefinitely, until data arrives. The <tt>settimeout</tt> function defines
a limit on the amount of time the functions can block. When a timeout is
set and the specified amount of time has elapsed, the affected methods
give up and fail with an error code.
</p>
<p class="parameters">
The amount of time to wait is specified as
the <tt>value</tt> parameter, in seconds. The <b><tt>nil</tt></b> timeout
<tt>value</tt> allows operations to block indefinitely. Negative
timeout values have the same effect.
</p>
<p class="note">
Note: In UDP, the <a href="#send"><tt>send</tt></a>
and <a href="#sentdo"><tt>sendto</tt></a> methods never block (the
datagram is just passed to the OS and the call returns
immediately). Therefore, the <tt>settimeout</tt> method has no
effect on them.
</p>
<p class="note">
Note: The old <tt>timeout</tt> method is
deprecated. The name has been changed for sake of uniformity, since
all other method names already contained verbs making their
imperative nature obvious.
</p>
<!-- footer ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:26:01 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

328
doc/url.html Normal file
View File

@ -0,0 +1,328 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta name="description" content="LuaSocket: URL manipulation">
<meta name="keywords" content="Lua, LuaSocket, URL, Library, Link, Network, Support">
<title>LuaSocket: URL support</title>
<link rel="stylesheet" href="reference.css" type="text/css">
</head>
<body>
<!-- header +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=header>
<hr>
<center>
<table summary="LuaSocket logo">
<tr><td align=center><a href="http://www.lua.org">
<img width=128 height=128 border=0 alt="LuaSocket" src="luasocket.png">
</a></td></tr>
<tr><td align=center valign=top>Network support for the Lua language
</td></tr>
</table>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#download">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
</center>
<hr>
</div>
<!-- url ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<h2 id=url>URL</h2>
<p>
The <tt>url</tt> namespace provides functions to parse, protect,
and build URLs, as well as functions to compose absolute URLs
from base and relative URLs, according to
<a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
</p>
<p>
To obtain the <tt>url</tt> namespace, run:
</p>
<pre class=example>
-- loads the URL module
local url = require("socket.url")
</pre>
<p>
An URL is defined by the following grammar:
</p>
<blockquote>
<tt>
&lt;url&gt; ::= [&lt;scheme&gt;:][//&lt;authority&gt;][/&lt;path&gt;][;&lt;params&gt;][?&lt;query&gt;][#&lt;fragment&gt;]<br>
&lt;authority&gt; ::= [&lt;userinfo&gt;@]&lt;host&gt;[:&lt;port&gt;]<br>
&lt;userinfo&gt; ::= &lt;user&gt;[:&lt;password&gt;]<br>
&lt;path&gt; ::= {&lt;segment&gt;/}&lt;segment&gt;<br>
</tt>
</blockquote>
<!-- absolute +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=absolute>
url.<b>absolute(</b>base, relative<b>)</b>
</p>
<p class=description>
Builds an absolute URL from a base URL and a relative URL.
</p>
<p class=parameters>
<tt>Base</tt> is a string with the base URL or
a parsed URL table. <tt>Relative</tt> is a
string with the relative URL.
</p>
<p class=return>
The function returns a string with the absolute URL.
</p>
<p class=note>
Note: The rules that
govern the composition are fairly complex, and are described in detail in
<a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>.
The example bellow should give an idea of what the rules are.
</p>
<pre class=example>
http://a/b/c/d;p?q
+
g:h = g:h
g = http://a/b/c/g
./g = http://a/b/c/g
g/ = http://a/b/c/g/
/g = http://a/g
//g = http://g
?y = http://a/b/c/?y
g?y = http://a/b/c/g?y
#s = http://a/b/c/d;p?q#s
g#s = http://a/b/c/g#s
g?y#s = http://a/b/c/g?y#s
;x = http://a/b/c/;x
g;x = http://a/b/c/g;x
g;x?y#s = http://a/b/c/g;x?y#s
. = http://a/b/c/
./ = http://a/b/c/
.. = http://a/b/
../ = http://a/b/
../g = http://a/b/g
../.. = http://a/
../../ = http://a/
../../g = http://a/g
</pre>
<!-- build ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=build>
url.<b>build(</b>parsed_url<b>)</b>
</p>
<p class=description>
Rebuilds an URL from its parts.
</p>
<p class=parameters>
<tt>Parsed_url</tt> is a table with same components returned by
<a href="#parse"><tt>parse</tt></a>.
Lower level components, if specified,
take precedence over high level components of the URL grammar.
</p>
<p class=return>
The function returns a string with the built URL.
</p>
<!-- build_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=build_path>
url.<b>build_path(</b>segments, unsafe<b>)</b>
</p>
<p class=description>
Builds a <tt>&lt;path&gt;</tt> component from a list of
<tt>&lt;segment&gt;</tt> parts.
Before composition, any reserved characters found in a segment are escaped into
their protected form, so that the resulting path is a valid URL path
component.
</p>
<p class=parameters>
<tt>Segments</tt> is a list of strings with the <tt>&lt;segment&gt;</tt>
parts. If <tt>unsafe</tt> is anything but <b><tt>nil</tt></b>, reserved
characters are left untouched.
</p>
<p class=return>
The function returns a string with the
built <tt>&lt;path&gt;</tt> component.
</p>
<!-- escape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="escape">
url.<b>escape(</b>content<b>)</b>
</p>
<p class=description>
Applies the URL escaping content coding to a string
Each byte is encoded as a percent character followed
by the two byte hexadecimal representation of its integer
value.
</p>
<p class=parameters>
<tt>Content</tt> is the string to be encoded.
</p>
<p class=result>
The function returns the encoded string.
</p>
<pre class=example>
-- load url module
url = require("socket.url")
code = url.escape("/#?;")
-- code = "%2f%23%3f%3b"
</pre>
<!-- parse ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=parse>
url.<b>parse(</b>url, default<b>)</b>
</p>
<p class=description>
Parses an URL given as a string into a Lua table with its components.
</p>
<p class=parameters>
<tt>Url</tt> is the URL to be parsed. If the <tt>default</tt> table is
present, it is used to store the parsed fields. Only fields present in the
URL are overwritten. Therefore, this table can be used to pass default
values for each field.
</p>
<p class=return>
The function returns a table with all the URL components:
</p>
<blockquote><tt>
parsed_url = {<br>
&nbsp;&nbsp;url = <i>string</i>,<br>
&nbsp;&nbsp;scheme = <i>string</i>,<br>
&nbsp;&nbsp;authority = <i>string</i>,<br>
&nbsp;&nbsp;path = <i>string</i>,<br>
&nbsp;&nbsp;params = <i>string</i>,<br>
&nbsp;&nbsp;query = <i>string</i>,<br>
&nbsp;&nbsp;fragment = <i>string</i>,<br>
&nbsp;&nbsp;userinfo = <i>string</i>,<br>
&nbsp;&nbsp;host = <i>string</i>,<br>
&nbsp;&nbsp;port = <i>string</i>,<br>
&nbsp;&nbsp;user = <i>string</i>,<br>
&nbsp;&nbsp;password = <i>string</i><br>
}
</tt></blockquote>
<pre class=example>
-- load url module
url = require("socket.url")
parsed_url = url.parse("http://www.example.com/cgilua/index.lua?a=2#there")
-- parsed_url = {
-- scheme = "http",
-- authority = "www.example.com",
-- path = "/cgilua/index.lua"
-- query = "a=2",
-- fragment = "there",
-- host = "www.puc-rio.br",
-- }
parsed_url = url.parse("ftp://root:passwd@unsafe.org/pub/virus.exe;type=i")
-- parsed_url = {
-- scheme = "ftp",
-- authority = "root:passwd@unsafe.org",
-- path = "/pub/virus.exe",
-- params = "type=i",
-- userinfo = "root:passwd",
-- host = "unsafe.org",
-- user = "root",
-- password = "passwd",
-- }
</pre>
<!-- parse_path +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=parse_path>
url.<b>parse_path(</b>path<b>)</b>
</p>
<p class=description>
Breaks a <tt>&lt;path&gt;</tt> URL component into all its
<tt>&lt;segment&gt;</tt> parts.
</p>
<p class=description>
<tt>Path</tt> is a string with the path to be parsed.
</p>
<p class=return>
Since some characters are reserved in URLs, they must be escaped
whenever present in a <tt>&lt;path&gt;</tt> component. Therefore, before
returning a list with all the parsed segments, the function removes
escaping from all of them.
</p>
<!-- unescape +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id="unescape">
url.<b>unescape(</b>content<b>)</b>
</p>
<p class=description>
Removes the URL escaping content coding from a string.
</p>
<p class=parameters>
<tt>Content</tt> is the string to be decoded.
</p>
<p class=return>
The function returns the decoded string.
</p>
<!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<div class=footer>
<hr>
<center>
<p class=bar>
<a href="index.html">home</a> &middot;
<a href="index.html#down">download</a> &middot;
<a href="installation.html">installation</a> &middot;
<a href="introduction.html">introduction</a> &middot;
<a href="reference.html">reference</a>
</p>
<p>
<small>
Last modified by Diego Nehab on <br>
Thu Apr 20 00:26:05 EDT 2006
</small>
</p>
</center>
</div>
</body>
</html>

89
etc/README Normal file
View File

@ -0,0 +1,89 @@
This directory contains code that is more useful than the
samples. This code *is* supported.
tftp.lua -- Trivial FTP client
This module implements file retrieval by the TFTP protocol.
Its main use was to test the UDP code, but since someone
found it usefull, I turned it into a module that is almost
official (no uploads, yet).
dict.lua -- Dict client
The dict.lua module started with a cool simple client
for the DICT protocol, written by Luiz Henrique Figueiredo.
This new version has been converted into a library, similar
to the HTTP and FTP libraries, that can be used from within
any luasocket application. Take a look on the source code
and you will be able to figure out how to use it.
lp.lua -- LPD client library
The lp.lua module implements the client part of the Line
Printer Daemon protocol, used to print files on Unix
machines. It is courtesy of David Burgess! See the source
code and the lpr.lua in the examples directory.
b64.lua
qp.lua
eol.lua
These are tiny programs that perform Base64,
Quoted-Printable and end-of-line marker conversions.
get.lua -- file retriever
This little program is a client that uses the FTP and
HTTP code to implement a command line file graber. Just
run
lua get.lua <remote-file> [<local-file>]
to download a remote file (either ftp:// or http://) to
the specified local file. The program also prints the
download throughput, elapsed time, bytes already downloaded
etc during download.
check-memory.lua -- checks memory consumption
This is just to see how much memory each module uses.
dispatch.lua -- coroutine based dispatcher
This is a first try at a coroutine based non-blocking
dispatcher for LuaSocket. Take a look at 'check-links.lua'
and at 'forward.lua' to see how to use it.
check-links.lua -- HTML link checker program
This little program scans a HTML file and checks for broken
links. It is similar to check-links.pl by Jamie Zawinski,
but uses all facilities of the LuaSocket library and the Lua
language. It has not been thoroughly tested, but it should
work. Just run
lua check-links.lua [-n] {<url>} > output
and open the result to see a list of broken links. Make sure
you check the '-n' switch. It runs in non-blocking mode,
using coroutines, and is MUCH faster!
forward.lua -- coroutine based forward server
This is a forward server that can accept several connections
and transfers simultaneously using non-blocking I/O and the
coroutine-based dispatcher. You can run, for example
lua forward.lua 8080:proxy.com:3128
to redirect all local conections to port 8080 to the host
'proxy.com' at port 3128.
unix.c and unix.h
This is an implementation of Unix local domain sockets and
demonstrates how to extend LuaSocket with a new type of
transport. It has been tested on Linux and on Mac OS X.
Good luck,
Diego.

20
etc/b64.lua Normal file
View File

@ -0,0 +1,20 @@
-----------------------------------------------------------------------------
-- Little program to convert to and from Base64
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id: b64.lua,v 1.8 2004/06/16 04:28:21 diego Exp $
-----------------------------------------------------------------------------
local ltn12 = require("ltn12")
local mime = require("mime")
local source = ltn12.source.file(io.stdin)
local sink = ltn12.sink.file(io.stdout)
local convert
if arg and arg[1] == '-d' then
convert = mime.decode("base64")
else
local base64 = mime.encode("base64")
local wrap = mime.wrap()
convert = ltn12.filter.chain(base64, wrap)
end
sink = ltn12.sink.chain(convert, sink)
ltn12.pump.all(source, sink)

112
etc/check-links.lua Normal file
View File

@ -0,0 +1,112 @@
-----------------------------------------------------------------------------
-- Little program that checks links in HTML files, using coroutines and
-- non-blocking I/O via the dispatcher module.
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $$
-----------------------------------------------------------------------------
local url = require("socket.url")
local dispatch = require("dispatch")
local http = require("socket.http")
dispatch.TIMEOUT = 10
-- make sure the user knows how to invoke us
arg = arg or {}
if table.getn(arg) < 1 then
print("Usage:\n luasocket check-links.lua [-n] {<url>}")
exit()
end
-- '-n' means we are running in non-blocking mode
if arg[1] == "-n" then
-- if non-blocking I/O was requested, use real dispatcher interface
table.remove(arg, 1)
handler = dispatch.newhandler("coroutine")
else
-- if using blocking I/O, use fake dispatcher interface
handler = dispatch.newhandler("sequential")
end
local nthreads = 0
-- get the status of a URL using the dispatcher
function getstatus(link)
local parsed = url.parse(link, {scheme = "file"})
if parsed.scheme == "http" then
nthreads = nthreads + 1
handler:start(function()
local r, c, h, s = http.request{
method = "HEAD",
url = link,
create = handler.tcp
}
if r and c == 200 then io.write('\t', link, '\n')
else io.write('\t', link, ': ', tostring(c), '\n') end
nthreads = nthreads - 1
end)
end
end
function readfile(path)
path = url.unescape(path)
local file, error = io.open(path, "r")
if file then
local body = file:read("*a")
file:close()
return body
else return nil, error end
end
function load(u)
local parsed = url.parse(u, { scheme = "file" })
local body, headers, code, error
local base = u
if parsed.scheme == "http" then
body, code, headers = http.request(u)
if code == 200 then
-- if there was a redirect, update base to reflect it
base = headers.location or base
end
if not body then
error = code
end
elseif parsed.scheme == "file" then
body, error = readfile(parsed.path)
else error = string.format("unhandled scheme '%s'", parsed.scheme) end
return base, body, error
end
function getlinks(body, base)
-- get rid of comments
body = string.gsub(body, "%<%!%-%-.-%-%-%>", "")
local links = {}
-- extract links
body = string.gsub(body, '[Hh][Rr][Ee][Ff]%s*=%s*"([^"]*)"', function(href)
table.insert(links, url.absolute(base, href))
end)
body = string.gsub(body, "[Hh][Rr][Ee][Ff]%s*=%s*'([^']*)'", function(href)
table.insert(links, url.absolute(base, href))
end)
string.gsub(body, "[Hh][Rr][Ee][Ff]%s*=%s*(.-)>", function(href)
table.insert(links, url.absolute(base, href))
end)
return links
end
function checklinks(address)
local base, body, error = load(address)
if not body then print(error) return end
print("Checking ", base)
local links = getlinks(body, base)
for _, link in ipairs(links) do
getstatus(link)
end
end
for _, address in ipairs(arg) do
checklinks(url.absolute("file:", address))
end
while nthreads > 0 do
handler:step()
end

17
etc/check-memory.lua Normal file
View File

@ -0,0 +1,17 @@
function load(s)
collectgarbage()
local a = gcinfo()
_G[s] = require(s)
collectgarbage()
local b = gcinfo()
print(s .. ":\t " .. (b-a) .. "k")
end
load("socket.url")
load("ltn12")
load("socket")
load("mime")
load("socket.tp")
load("socket.smtp")
load("socket.http")
load("socket.ftp")

152
etc/dict.lua Normal file
View File

@ -0,0 +1,152 @@
-----------------------------------------------------------------------------
-- Little program to download DICT word definitions
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id: dict.lua,v 1.22 2005/11/22 08:33:29 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Load required modules
-----------------------------------------------------------------------------
local base = _G
local string = require("string")
local table = require("table")
local socket = require("socket")
local url = require("socket.url")
local tp = require("socket.tp")
module("socket.dict")
-----------------------------------------------------------------------------
-- Globals
-----------------------------------------------------------------------------
HOST = "dict.org"
PORT = 2628
TIMEOUT = 10
-----------------------------------------------------------------------------
-- Low-level dict API
-----------------------------------------------------------------------------
local metat = { __index = {} }
function open(host, port)
local tp = socket.try(tp.connect(host or HOST, port or PORT, TIMEOUT))
return base.setmetatable({tp = tp}, metat)
end
function metat.__index:greet()
return socket.try(self.tp:check(220))
end
function metat.__index:check(ok)
local code, status = socket.try(self.tp:check(ok))
return code,
base.tonumber(socket.skip(2, string.find(status, "^%d%d%d (%d*)")))
end
function metat.__index:getdef()
local line = socket.try(self.tp:receive())
local def = {}
while line ~= "." do
table.insert(def, line)
line = socket.try(self.tp:receive())
end
return table.concat(def, "\n")
end
function metat.__index:define(database, word)
database = database or "!"
socket.try(self.tp:command("DEFINE", database .. " " .. word))
local code, count = self:check(150)
local defs = {}
for i = 1, count do
self:check(151)
table.insert(defs, self:getdef())
end
self:check(250)
return defs
end
function metat.__index:match(database, strat, word)
database = database or "!"
strat = strat or "."
socket.try(self.tp:command("MATCH", database .." ".. strat .." ".. word))
self:check(152)
local mat = {}
local line = socket.try(self.tp:receive())
while line ~= '.' do
database, word = socket.skip(2, string.find(line, "(%S+) (.*)"))
if not mat[database] then mat[database] = {} end
table.insert(mat[database], word)
line = socket.try(self.tp:receive())
end
self:check(250)
return mat
end
function metat.__index:quit()
self.tp:command("QUIT")
return self:check(221)
end
function metat.__index:close()
return self.tp:close()
end
-----------------------------------------------------------------------------
-- High-level dict API
-----------------------------------------------------------------------------
local default = {
scheme = "dict",
host = "dict.org"
}
local function there(f)
if f == "" then return nil
else return f end
end
local function parse(u)
local t = socket.try(url.parse(u, default))
socket.try(t.scheme == "dict", "invalid scheme '" .. t.scheme .. "'")
socket.try(t.path, "invalid path in url")
local cmd, arg = socket.skip(2, string.find(t.path, "^/(.)(.*)$"))
socket.try(cmd == "d" or cmd == "m", "<command> should be 'm' or 'd'")
socket.try(arg and arg ~= "", "need at least <word> in URL")
t.command, t.argument = cmd, arg
arg = string.gsub(arg, "^:([^:]+)", function(f) t.word = f end)
socket.try(t.word, "need at least <word> in URL")
arg = string.gsub(arg, "^:([^:]*)", function(f) t.database = there(f) end)
if cmd == "m" then
arg = string.gsub(arg, "^:([^:]*)", function(f) t.strat = there(f) end)
end
string.gsub(arg, ":([^:]*)$", function(f) t.n = base.tonumber(f) end)
return t
end
local function tget(gett)
local con = open(gett.host, gett.port)
con:greet()
if gett.command == "d" then
local def = con:define(gett.database, gett.word)
con:quit()
con:close()
if gett.n then return def[gett.n]
else return def end
elseif gett.command == "m" then
local mat = con:match(gett.database, gett.strat, gett.word)
con:quit()
con:close()
return mat
else return nil, "invalid command" end
end
local function sget(u)
local gett = parse(u)
return tget(gett)
end
get = socket.protect(function(gett)
if base.type(gett) == "string" then return sget(gett)
else return tget(gett) end
end)

302
etc/dispatch.lua Normal file
View File

@ -0,0 +1,302 @@
-----------------------------------------------------------------------------
-- A hacked dispatcher module
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $$
-----------------------------------------------------------------------------
local base = _G
local table = require("table")
local socket = require("socket")
local coroutine = require("coroutine")
module("dispatch")
-- if too much time goes by without any activity in one of our sockets, we
-- just kill it
TIMEOUT = 60
-----------------------------------------------------------------------------
-- We implement 3 types of dispatchers:
-- sequential
-- coroutine
-- threaded
-- The user can choose whatever one is needed
-----------------------------------------------------------------------------
local handlert = {}
-- default handler is coroutine
function newhandler(mode)
mode = mode or "coroutine"
return handlert[mode]()
end
local function seqstart(self, func)
return func()
end
-- sequential handler simply calls the functions and doesn't wrap I/O
function handlert.sequential()
return {
tcp = socket.tcp,
start = seqstart
}
end
-----------------------------------------------------------------------------
-- Mega hack. Don't try to do this at home.
-----------------------------------------------------------------------------
-- we can't yield across calls to protect, so we rewrite it with coxpcall
-- make sure you don't require any module that uses socket.protect before
-- loading our hack
function socket.protect(f)
return function(...)
local co = coroutine.create(f)
while true do
local results = {coroutine.resume(co, base.unpack(arg))}
local status = table.remove(results, 1)
if not status then
if base.type(results[1]) == 'table' then
return nil, results[1][1]
else base.error(results[1]) end
end
if coroutine.status(co) == "suspended" then
arg = {coroutine.yield(base.unpack(results))}
else
return base.unpack(results)
end
end
end
end
-----------------------------------------------------------------------------
-- Simple set data structure. O(1) everything.
-----------------------------------------------------------------------------
local function newset()
local reverse = {}
local set = {}
return base.setmetatable(set, {__index = {
insert = function(set, value)
if not reverse[value] then
table.insert(set, value)
reverse[value] = table.getn(set)
end
end,
remove = function(set, value)
local index = reverse[value]
if index then
reverse[value] = nil
local top = table.remove(set)
if top ~= value then
reverse[top] = index
set[index] = top
end
end
end
}})
end
-----------------------------------------------------------------------------
-- socket.tcp() wrapper for the coroutine dispatcher
-----------------------------------------------------------------------------
local function cowrap(dispatcher, tcp, error)
if not tcp then return nil, error end
-- put it in non-blocking mode right away
tcp:settimeout(0)
-- metatable for wrap produces new methods on demand for those that we
-- don't override explicitly.
local metat = { __index = function(table, key)
table[key] = function(...)
arg[1] = tcp
return tcp[key](base.unpack(arg))
end
return table[key]
end}
-- does our user want to do his own non-blocking I/O?
local zero = false
-- create a wrap object that will behave just like a real socket object
local wrap = { }
-- we ignore settimeout to preserve our 0 timeout, but record whether
-- the user wants to do his own non-blocking I/O
function wrap:settimeout(value, mode)
if value == 0 then zero = true
else zero = false end
return 1
end
-- send in non-blocking mode and yield on timeout
function wrap:send(data, first, last)
first = (first or 1) - 1
local result, error
while true do
-- return control to dispatcher and tell it we want to send
-- if upon return the dispatcher tells us we timed out,
-- return an error to whoever called us
if coroutine.yield(dispatcher.sending, tcp) == "timeout" then
return nil, "timeout"
end
-- try sending
result, error, first = tcp:send(data, first+1, last)
-- if we are done, or there was an unexpected error,
-- break away from loop
if error ~= "timeout" then return result, error, first end
end
end
-- receive in non-blocking mode and yield on timeout
-- or simply return partial read, if user requested timeout = 0
function wrap:receive(pattern, partial)
local error = "timeout"
local value
while true do
-- return control to dispatcher and tell it we want to receive
-- if upon return the dispatcher tells us we timed out,
-- return an error to whoever called us
if coroutine.yield(dispatcher.receiving, tcp) == "timeout" then
return nil, "timeout"
end
-- try receiving
value, error, partial = tcp:receive(pattern, partial)
-- if we are done, or there was an unexpected error,
-- break away from loop. also, if the user requested
-- zero timeout, return all we got
if (error ~= "timeout") or zero then
return value, error, partial
end
end
end
-- connect in non-blocking mode and yield on timeout
function wrap:connect(host, port)
local result, error = tcp:connect(host, port)
if error == "timeout" then
-- return control to dispatcher. we will be writable when
-- connection succeeds.
-- if upon return the dispatcher tells us we have a
-- timeout, just abort
if coroutine.yield(dispatcher.sending, tcp) == "timeout" then
return nil, "timeout"
end
-- when we come back, check if connection was successful
result, error = tcp:connect(host, port)
if result or error == "already connected" then return 1
else return nil, "non-blocking connect failed" end
else return result, error end
end
-- accept in non-blocking mode and yield on timeout
function wrap:accept()
while 1 do
-- return control to dispatcher. we will be readable when a
-- connection arrives.
-- if upon return the dispatcher tells us we have a
-- timeout, just abort
if coroutine.yield(dispatcher.receiving, tcp) == "timeout" then
return nil, "timeout"
end
local client, error = tcp:accept()
if error ~= "timeout" then
return cowrap(dispatcher, client, error)
end
end
end
-- remove cortn from context
function wrap:close()
dispatcher.stamp[tcp] = nil
dispatcher.sending.set:remove(tcp)
dispatcher.sending.cortn[tcp] = nil
dispatcher.receiving.set:remove(tcp)
dispatcher.receiving.cortn[tcp] = nil
return tcp:close()
end
return base.setmetatable(wrap, metat)
end
-----------------------------------------------------------------------------
-- Our coroutine dispatcher
-----------------------------------------------------------------------------
local cometat = { __index = {} }
function schedule(cortn, status, operation, tcp)
if status then
if cortn and operation then
operation.set:insert(tcp)
operation.cortn[tcp] = cortn
operation.stamp[tcp] = socket.gettime()
end
else base.error(operation) end
end
function kick(operation, tcp)
operation.cortn[tcp] = nil
operation.set:remove(tcp)
end
function wakeup(operation, tcp)
local cortn = operation.cortn[tcp]
-- if cortn is still valid, wake it up
if cortn then
kick(operation, tcp)
return cortn, coroutine.resume(cortn)
-- othrewise, just get scheduler not to do anything
else
return nil, true
end
end
function abort(operation, tcp)
local cortn = operation.cortn[tcp]
if cortn then
kick(operation, tcp)
coroutine.resume(cortn, "timeout")
end
end
-- step through all active cortns
function cometat.__index:step()
-- check which sockets are interesting and act on them
local readable, writable = socket.select(self.receiving.set,
self.sending.set, 1)
-- for all readable connections, resume their cortns and reschedule
-- when they yield back to us
for _, tcp in base.ipairs(readable) do
schedule(wakeup(self.receiving, tcp))
end
-- for all writable connections, do the same
for _, tcp in base.ipairs(writable) do
schedule(wakeup(self.sending, tcp))
end
-- politely ask replacement I/O functions in idle cortns to
-- return reporting a timeout
local now = socket.gettime()
for tcp, stamp in base.pairs(self.stamp) do
if tcp.class == "tcp{client}" and now - stamp > TIMEOUT then
abort(self.sending, tcp)
abort(self.receiving, tcp)
end
end
end
function cometat.__index:start(func)
local cortn = coroutine.create(func)
schedule(cortn, coroutine.resume(cortn))
end
function handlert.coroutine()
local stamp = {}
local dispatcher = {
stamp = stamp,
sending = {
name = "sending",
set = newset(),
cortn = {},
stamp = stamp
},
receiving = {
name = "receiving",
set = newset(),
cortn = {},
stamp = stamp
},
}
function dispatcher.tcp()
return cowrap(dispatcher, socket.tcp())
end
return base.setmetatable(dispatcher, cometat)
end

14
etc/eol.lua Normal file
View File

@ -0,0 +1,14 @@
-----------------------------------------------------------------------------
-- Little program to adjust end of line markers.
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id: eol.lua,v 1.8 2005/11/22 08:33:29 diego Exp $
-----------------------------------------------------------------------------
local mime = require("mime")
local ltn12 = require("ltn12")
local marker = '\n'
if arg and arg[1] == '-d' then marker = '\r\n' end
local filter = mime.normalize(marker)
local source = ltn12.source.chain(ltn12.source.file(io.stdin), filter)
local sink = ltn12.sink.file(io.stdout)
ltn12.pump.all(source, sink)

65
etc/forward.lua Normal file
View File

@ -0,0 +1,65 @@
-- load our favourite library
local dispatch = require("dispatch")
local handler = dispatch.newhandler()
-- make sure the user knows how to invoke us
if table.getn(arg) < 1 then
print("Usage")
print(" lua forward.lua <iport:ohost:oport> ...")
os.exit(1)
end
-- function to move data from one socket to the other
local function move(foo, bar)
local live
while 1 do
local data, error, partial = foo:receive(2048)
live = data or error == "timeout"
data = data or partial
local result, error = bar:send(data)
if not live or not result then
foo:close()
bar:close()
break
end
end
end
-- for each tunnel, start a new server
for i, v in ipairs(arg) do
-- capture forwarding parameters
local _, _, iport, ohost, oport = string.find(v, "([^:]+):([^:]+):([^:]+)")
assert(iport, "invalid arguments")
-- create our server socket
local server = assert(handler.tcp())
assert(server:setoption("reuseaddr", true))
assert(server:bind("*", iport))
assert(server:listen(32))
-- handler for the server object loops accepting new connections
handler:start(function()
while 1 do
local client = assert(server:accept())
assert(client:settimeout(0))
-- for each new connection, start a new client handler
handler:start(function()
-- handler tries to connect to peer
local peer = assert(handler.tcp())
assert(peer:settimeout(0))
assert(peer:connect(ohost, oport))
-- if sucessful, starts a new handler to send data from
-- client to peer
handler:start(function()
move(client, peer)
end)
-- afte starting new handler, enter in loop sending data from
-- peer to client
move(peer, client)
end)
end
end)
end
-- simply loop stepping the server
while 1 do
handler:step()
end

142
etc/get.lua Normal file
View File

@ -0,0 +1,142 @@
-----------------------------------------------------------------------------
-- Little program to download files from URLs
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id: get.lua,v 1.25 2007/03/12 04:08:40 diego Exp $
-----------------------------------------------------------------------------
local socket = require("socket")
local http = require("socket.http")
local ftp = require("socket.ftp")
local url = require("socket.url")
local ltn12 = require("ltn12")
-- formats a number of seconds into human readable form
function nicetime(s)
local l = "s"
if s > 60 then
s = s / 60
l = "m"
if s > 60 then
s = s / 60
l = "h"
if s > 24 then
s = s / 24
l = "d" -- hmmm
end
end
end
if l == "s" then return string.format("%5.0f%s", s, l)
else return string.format("%5.2f%s", s, l) end
end
-- formats a number of bytes into human readable form
function nicesize(b)
local l = "B"
if b > 1024 then
b = b / 1024
l = "KB"
if b > 1024 then
b = b / 1024
l = "MB"
if b > 1024 then
b = b / 1024
l = "GB" -- hmmm
end
end
end
return string.format("%7.2f%2s", b, l)
end
-- returns a string with the current state of the download
local remaining_s = "%s received, %s/s throughput, %2.0f%% done, %s remaining"
local elapsed_s = "%s received, %s/s throughput, %s elapsed "
function gauge(got, delta, size)
local rate = got / delta
if size and size >= 1 then
return string.format(remaining_s, nicesize(got), nicesize(rate),
100*got/size, nicetime((size-got)/rate))
else
return string.format(elapsed_s, nicesize(got),
nicesize(rate), nicetime(delta))
end
end
-- creates a new instance of a receive_cb that saves to disk
-- kind of copied from luasocket's manual callback examples
function stats(size)
local start = socket.gettime()
local last = start
local got = 0
return function(chunk)
-- elapsed time since start
local current = socket.gettime()
if chunk then
-- total bytes received
got = got + string.len(chunk)
-- not enough time for estimate
if current - last > 1 then
io.stderr:write("\r", gauge(got, current - start, size))
io.stderr:flush()
last = current
end
else
-- close up
io.stderr:write("\r", gauge(got, current - start), "\n")
end
return chunk
end
end
-- determines the size of a http file
function gethttpsize(u)
local r, c, h = http.request {method = "HEAD", url = u}
if c == 200 then
return tonumber(h["content-length"])
end
end
-- downloads a file using the http protocol
function getbyhttp(u, file)
local save = ltn12.sink.file(file or io.stdout)
-- only print feedback if output is not stdout
if file then save = ltn12.sink.chain(stats(gethttpsize(u)), save) end
local r, c, h, s = http.request {url = u, sink = save }
if c ~= 200 then io.stderr:write(s or c, "\n") end
end
-- downloads a file using the ftp protocol
function getbyftp(u, file)
local save = ltn12.sink.file(file or io.stdout)
-- only print feedback if output is not stdout
-- and we don't know how big the file is
if file then save = ltn12.sink.chain(stats(), save) end
local gett = url.parse(u)
gett.sink = save
gett.type = "i"
local ret, err = ftp.get(gett)
if err then print(err) end
end
-- determines the scheme
function getscheme(u)
-- this is an heuristic to solve a common invalid url poblem
if not string.find(u, "//") then u = "//" .. u end
local parsed = url.parse(u, {scheme = "http"})
return parsed.scheme
end
-- gets a file either by http or ftp, saving as <name>
function get(u, name)
local fout = name and io.open(name, "wb")
local scheme = getscheme(u)
if scheme == "ftp" then getbyftp(u, fout)
elseif scheme == "http" then getbyhttp(u, fout)
else print("unknown scheme" .. scheme) end
end
-- main program
arg = arg or {}
if table.getn(arg) < 1 then
io.write("Usage:\n lua get.lua <remote-url> [<local-file>]\n")
os.exit(1)
else get(arg[1], arg[2]) end

324
etc/lp.lua Normal file
View File

@ -0,0 +1,324 @@
-----------------------------------------------------------------------------
-- LPD support for the Lua language
-- LuaSocket toolkit.
-- Author: David Burgess
-- Modified by Diego Nehab, but David is in charge
-- RCS ID: $Id: lp.lua,v 1.14 2005/11/21 07:04:44 diego Exp $
-----------------------------------------------------------------------------
--[[
if you have any questions: RFC 1179
]]
-- make sure LuaSocket is loaded
local io = require("io")
local base = _G
local os = require("os")
local math = require("math")
local string = require("string")
local socket = require("socket")
local ltn12 = require("ltn12")
module("socket.lp")
-- default port
PORT = 515
SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost"
PRINTER = os.getenv("PRINTER") or "printer"
local function connect(localhost, option)
local host = option.host or SERVER
local port = option.port or PORT
local skt
local try = socket.newtry(function() if skt then skt:close() end end)
if option.localbind then
-- bind to a local port (if we can)
local localport = 721
local done, err
repeat
skt = socket.try(socket.tcp())
try(skt:settimeout(30))
done, err = skt:bind(localhost, localport)
if not done then
localport = localport + 1
skt:close()
skt = nil
else break end
until localport > 731
socket.try(skt, err)
else skt = socket.try(socket.tcp()) end
try(skt:connect(host, port))
return { skt = skt, try = try }
end
--[[
RFC 1179
5.3 03 - Send queue state (short)
+----+-------+----+------+----+
| 03 | Queue | SP | List | LF |
+----+-------+----+------+----+
Command code - 3
Operand 1 - Printer queue name
Other operands - User names or job numbers
If the user names or job numbers or both are supplied then only those
jobs for those users or with those numbers will be sent.
The response is an ASCII stream which describes the printer queue.
The stream continues until the connection closes. Ends of lines are
indicated with ASCII LF control characters. The lines may also
contain ASCII HT control characters.
5.4 04 - Send queue state (long)
+----+-------+----+------+----+
| 04 | Queue | SP | List | LF |
+----+-------+----+------+----+
Command code - 4
Operand 1 - Printer queue name
Other operands - User names or job numbers
If the user names or job numbers or both are supplied then only those
jobs for those users or with those numbers will be sent.
The response is an ASCII stream which describes the printer queue.
The stream continues until the connection closes. Ends of lines are
indicated with ASCII LF control characters. The lines may also
contain ASCII HT control characters.
]]
-- gets server acknowledement
local function recv_ack(con)
local ack = con.skt:receive(1)
con.try(string.char(0) == ack, "failed to receive server acknowledgement")
end
-- sends client acknowledement
local function send_ack(con)
local sent = con.skt:send(string.char(0))
con.try(sent == 1, "failed to send acknowledgement")
end
-- sends queue request
-- 5.2 02 - Receive a printer job
--
-- +----+-------+----+
-- | 02 | Queue | LF |
-- +----+-------+----+
-- Command code - 2
-- Operand - Printer queue name
--
-- Receiving a job is controlled by a second level of commands. The
-- daemon is given commands by sending them over the same connection.
-- The commands are described in the next section (6).
--
-- After this command is sent, the client must read an acknowledgement
-- octet from the daemon. A positive acknowledgement is an octet of
-- zero bits. A negative acknowledgement is an octet of any other
-- pattern.
local function send_queue(con, queue)
queue = queue or PRINTER
local str = string.format("\2%s\10", queue)
local sent = con.skt:send(str)
con.try(sent == string.len(str), "failed to send print request")
recv_ack(con)
end
-- sends control file
-- 6.2 02 - Receive control file
--
-- +----+-------+----+------+----+
-- | 02 | Count | SP | Name | LF |
-- +----+-------+----+------+----+
-- Command code - 2
-- Operand 1 - Number of bytes in control file
-- Operand 2 - Name of control file
--
-- The control file must be an ASCII stream with the ends of lines
-- indicated by ASCII LF. The total number of bytes in the stream is
-- sent as the first operand. The name of the control file is sent as
-- the second. It should start with ASCII "cfA", followed by a three
-- digit job number, followed by the host name which has constructed the
-- control file. Acknowledgement processing must occur as usual after
-- the command is sent.
--
-- The next "Operand 1" octets over the same TCP connection are the
-- intended contents of the control file. Once all of the contents have
-- been delivered, an octet of zero bits is sent as an indication that
-- the file being sent is complete. A second level of acknowledgement
-- processing must occur at this point.
-- sends data file
-- 6.3 03 - Receive data file
--
-- +----+-------+----+------+----+
-- | 03 | Count | SP | Name | LF |
-- +----+-------+----+------+----+
-- Command code - 3
-- Operand 1 - Number of bytes in data file
-- Operand 2 - Name of data file
--
-- The data file may contain any 8 bit values at all. The total number
-- of bytes in the stream may be sent as the first operand, otherwise
-- the field should be cleared to 0. The name of the data file should
-- start with ASCII "dfA". This should be followed by a three digit job
-- number. The job number should be followed by the host name which has
-- constructed the data file. Interpretation of the contents of the
-- data file is determined by the contents of the corresponding control
-- file. If a data file length has been specified, the next "Operand 1"
-- octets over the same TCP connection are the intended contents of the
-- data file. In this case, once all of the contents have been
-- delivered, an octet of zero bits is sent as an indication that the
-- file being sent is complete. A second level of acknowledgement
-- processing must occur at this point.
local function send_hdr(con, control)
local sent = con.skt:send(control)
con.try(sent and sent >= 1 , "failed to send header file")
recv_ack(con)
end
local function send_control(con, control)
local sent = con.skt:send(control)
con.try(sent and sent >= 1, "failed to send control file")
send_ack(con)
end
local function send_data(con,fh,size)
local buf
while size > 0 do
buf,message = fh:read(8192)
if buf then
st = con.try(con.skt:send(buf))
size = size - st
else
con.try(size == 0, "file size mismatch")
end
end
recv_ack(con) -- note the double acknowledgement
send_ack(con)
recv_ack(con)
return size
end
--[[
local control_dflt = {
"H"..string.sub(socket.hostname,1,31).."\10", -- host
"C"..string.sub(socket.hostname,1,31).."\10", -- class
"J"..string.sub(filename,1,99).."\10", -- jobname
"L"..string.sub(user,1,31).."\10", -- print banner page
"I"..tonumber(indent).."\10", -- indent column count ('f' only)
"M"..string.sub(mail,1,128).."\10", -- mail when printed user@host
"N"..string.sub(filename,1,131).."\10", -- name of source file
"P"..string.sub(user,1,31).."\10", -- user name
"T"..string.sub(title,1,79).."\10", -- title for banner ('p' only)
"W"..tonumber(width or 132).."\10", -- width of print f,l,p only
"f"..file.."\10", -- formatted print (remove control chars)
"l"..file.."\10", -- print
"o"..file.."\10", -- postscript
"p"..file.."\10", -- pr format - requires T, L
"r"..file.."\10", -- fortran format
"U"..file.."\10", -- Unlink (data file only)
}
]]
-- generate a varying job number
local seq = 0
local function newjob(connection)
seq = seq + 1
return math.floor(socket.gettime() * 1000 + seq)%1000
end
local format_codes = {
binary = 'l',
text = 'f',
ps = 'o',
pr = 'p',
fortran = 'r',
l = 'l',
r = 'r',
o = 'o',
p = 'p',
f = 'f'
}
-- lp.send{option}
-- requires option.file
send = socket.protect(function(option)
socket.try(option and base.type(option) == "table", "invalid options")
local file = option.file
socket.try(file, "invalid file name")
local fh = socket.try(io.open(file,"rb"))
local datafile_size = fh:seek("end") -- get total size
fh:seek("set") -- go back to start of file
local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME")
or "localhost"
local con = connect(localhost, option)
-- format the control file
local jobno = newjob()
local localip = socket.dns.toip(localhost)
localhost = string.sub(localhost,1,31)
local user = string.sub(option.user or os.getenv("LPRUSER") or
os.getenv("USERNAME") or os.getenv("USER") or "anonymous", 1,31)
local lpfile = string.format("dfA%3.3d%-s", jobno, localhost);
local fmt = format_codes[option.format] or 'l'
local class = string.sub(option.class or localip or localhost,1,31)
local _,_,ctlfn = string.find(file,".*[%/%\\](.*)")
ctlfn = string.sub(ctlfn or file,1,131)
local cfile =
string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n",
localhost,
class,
option.job or "LuaSocket",
user,
fmt, lpfile,
lpfile,
ctlfn); -- mandatory part of ctl file
if (option.banner) then cfile = cfile .. 'L'..user..'\10' end
if (option.indent) then cfile = cfile .. 'I'..base.tonumber(option.indent)..'\10' end
if (option.mail) then cfile = cfile .. 'M'..string.sub((option.mail),1,128)..'\10' end
if (fmt == 'p' and option.title) then cfile = cfile .. 'T'..string.sub((option.title),1,79)..'\10' end
if ((fmt == 'p' or fmt == 'l' or fmt == 'f') and option.width) then
cfile = cfile .. 'W'..base.tonumber(option,width)..'\10'
end
con.skt:settimeout(option.timeout or 65)
-- send the queue header
send_queue(con, option.queue)
-- send the control file header
local cfilecmd = string.format("\2%d cfA%3.3d%-s\n",string.len(cfile), jobno, localhost);
send_hdr(con,cfilecmd)
-- send the control file
send_control(con,cfile)
-- send the data file header
local dfilecmd = string.format("\3%d dfA%3.3d%-s\n",datafile_size, jobno, localhost);
send_hdr(con,dfilecmd)
-- send the data file
send_data(con,fh,datafile_size)
fh:close()
con.skt:close();
return jobno, datafile_size
end)
--
-- lp.query({host=,queue=printer|'*', format='l'|'s', list=})
--
query = socket.protect(function(p)
p = p or {}
local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME")
or "localhost"
local con = connect(localhost,p)
local fmt
if string.sub(p.format or 's',1,1) == 's' then fmt = 3 else fmt = 4 end
con.try(con.skt:send(string.format("%c%s %s\n", fmt, p.queue or "*",
p.list or "")))
local data = con.try(con.skt:receive("*a"))
con.skt:close()
return data
end)

24
etc/qp.lua Normal file
View File

@ -0,0 +1,24 @@
-----------------------------------------------------------------------------
-- Little program to convert to and from Quoted-Printable
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id: qp.lua,v 1.5 2004/06/17 21:46:22 diego Exp $
-----------------------------------------------------------------------------
local ltn12 = require("ltn12")
local mime = require("mime")
local convert
arg = arg or {}
local mode = arg and arg[1] or "-et"
if mode == "-et" then
local normalize = mime.normalize()
local qp = mime.encode("quoted-printable")
local wrap = mime.wrap("quoted-printable")
convert = ltn12.filter.chain(normalize, qp, wrap)
elseif mode == "-eb" then
local qp = mime.encode("quoted-printable", "binary")
local wrap = mime.wrap("quoted-printable")
convert = ltn12.filter.chain(qp, wrap)
else convert = mime.decode("quoted-printable") end
local source = ltn12.source.chain(ltn12.source.file(io.stdin), convert)
local sink = ltn12.sink.file(io.stdout)
ltn12.pump.all(source, sink)

155
etc/tftp.lua Normal file
View File

@ -0,0 +1,155 @@
-----------------------------------------------------------------------------
-- TFTP support for the Lua language
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: tftp.lua,v 1.16 2005/11/22 08:33:29 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Load required files
-----------------------------------------------------------------------------
local base = _G
local table = require("table")
local math = require("math")
local string = require("string")
local socket = require("socket")
local ltn12 = require("ltn12")
local url = require("socket.url")
module("socket.tftp")
-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
local char = string.char
local byte = string.byte
PORT = 69
local OP_RRQ = 1
local OP_WRQ = 2
local OP_DATA = 3
local OP_ACK = 4
local OP_ERROR = 5
local OP_INV = {"RRQ", "WRQ", "DATA", "ACK", "ERROR"}
-----------------------------------------------------------------------------
-- Packet creation functions
-----------------------------------------------------------------------------
local function RRQ(source, mode)
return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0)
end
local function WRQ(source, mode)
return char(0, OP_RRQ) .. source .. char(0) .. mode .. char(0)
end
local function ACK(block)
local low, high
low = math.mod(block, 256)
high = (block - low)/256
return char(0, OP_ACK, high, low)
end
local function get_OP(dgram)
local op = byte(dgram, 1)*256 + byte(dgram, 2)
return op
end
-----------------------------------------------------------------------------
-- Packet analysis functions
-----------------------------------------------------------------------------
local function split_DATA(dgram)
local block = byte(dgram, 3)*256 + byte(dgram, 4)
local data = string.sub(dgram, 5)
return block, data
end
local function get_ERROR(dgram)
local code = byte(dgram, 3)*256 + byte(dgram, 4)
local msg
_,_, msg = string.find(dgram, "(.*)\000", 5)
return string.format("error code %d: %s", code, msg)
end
-----------------------------------------------------------------------------
-- The real work
-----------------------------------------------------------------------------
local function tget(gett)
local retries, dgram, sent, datahost, dataport, code
local last = 0
socket.try(gett.host, "missing host")
local con = socket.try(socket.udp())
local try = socket.newtry(function() con:close() end)
-- convert from name to ip if needed
gett.host = try(socket.dns.toip(gett.host))
con:settimeout(1)
-- first packet gives data host/port to be used for data transfers
local path = string.gsub(gett.path or "", "^/", "")
path = url.unescape(path)
retries = 0
repeat
sent = try(con:sendto(RRQ(path, "octet"), gett.host, gett.port))
dgram, datahost, dataport = con:receivefrom()
retries = retries + 1
until dgram or datahost ~= "timeout" or retries > 5
try(dgram, datahost)
-- associate socket with data host/port
try(con:setpeername(datahost, dataport))
-- default sink
local sink = gett.sink or ltn12.sink.null()
-- process all data packets
while 1 do
-- decode packet
code = get_OP(dgram)
try(code ~= OP_ERROR, get_ERROR(dgram))
try(code == OP_DATA, "unhandled opcode " .. code)
-- get data packet parts
local block, data = split_DATA(dgram)
-- if not repeated, write
if block == last+1 then
try(sink(data))
last = block
end
-- last packet brings less than 512 bytes of data
if string.len(data) < 512 then
try(con:send(ACK(block)))
try(con:close())
try(sink(nil))
return 1
end
-- get the next packet
retries = 0
repeat
sent = try(con:send(ACK(last)))
dgram, err = con:receive()
retries = retries + 1
until dgram or err ~= "timeout" or retries > 5
try(dgram, err)
end
end
local default = {
port = PORT,
path ="/",
scheme = "tftp"
}
local function parse(u)
local t = socket.try(url.parse(u, default))
socket.try(t.scheme == "tftp", "invalid scheme '" .. t.scheme .. "'")
socket.try(t.host, "invalid host")
return t
end
local function sget(u)
local gett = parse(u)
local t = {}
gett.sink = ltn12.sink.table(t)
tget(gett)
return table.concat(t)
end
get = socket.protect(function(gett)
if base.type(gett) == "string" then return sget(gett)
else return tget(gett) end
end)

25
luasocket.sln Normal file
View File

@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "socket", "socket.vcproj", "{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mime", "mime.vcproj", "{128E8BD0-174A-48F0-8771-92B1E8D18713}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Debug|Win32.ActiveCfg = Debug|Win32
{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Debug|Win32.Build.0 = Debug|Win32
{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Release|Win32.ActiveCfg = Release|Win32
{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}.Release|Win32.Build.0 = Release|Win32
{128E8BD0-174A-48F0-8771-92B1E8D18713}.Debug|Win32.ActiveCfg = Debug|Win32
{128E8BD0-174A-48F0-8771-92B1E8D18713}.Debug|Win32.Build.0 = Debug|Win32
{128E8BD0-174A-48F0-8771-92B1E8D18713}.Release|Win32.ActiveCfg = Release|Win32
{128E8BD0-174A-48F0-8771-92B1E8D18713}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

15
makefile Normal file
View File

@ -0,0 +1,15 @@
PLAT?= macosx
PLATS= macosx linux win32
#------
# Hopefully no need to change anything below this line
#
all: $(PLAT)
$(PLATS) none install local clean:
@cd src; $(MAKE) $@
test: dummy
lua test/hello.lua
.PHONY: dummy

207
mime.vcproj Normal file
View File

@ -0,0 +1,207 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="mime"
ProjectGUID="{128E8BD0-174A-48F0-8771-92B1E8D18713}"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="src"
IntermediateDirectory="src"
ConfigurationType="2"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\lua-5.1.4\src"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MIME_EXPORTS;MIME_API=__declspec(dllexport);_CRT_SECURE_NO_WARNINGS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="lua5.1.lib"
OutputFile="$(OutDir)/mime.dll"
LinkIncremental="2"
AdditionalLibraryDirectories="..\lua-5.1.4"
GenerateDebugInformation="true"
ProgramDatabaseFile="$(OutDir)/mime.pdb"
SubSystem="2"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
ImportLibrary="$(OutDir)/mime.lib"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="src"
IntermediateDirectory="src"
ConfigurationType="2"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\lua-5.1.4\src"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MIME_EXPORTS; MIME_API=__declspec(dllexport);_CRT_SECURE_NO_WARNINGS"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="lua5.1.lib"
OutputFile="$(OutDir)/mime.dll"
LinkIncremental="1"
AdditionalLibraryDirectories="..\lua-5.1.4"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
ImportLibrary="$(OutDir)/mime.lib"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="src\mime.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

50
samples/README Normal file
View File

@ -0,0 +1,50 @@
This directory contains some sample programs using
LuaSocket. This code is not supported.
listener.lua -- socket to stdout
talker.lua -- stdin to socket
listener.lua and talker.lua are about the simplest
applications you can write using LuaSocket. Run
'lua listener.lua' and 'lua talker.lua'
on different terminals. Whatever you type on talk.lua will
be printed by listen.lua.
lpr.lua -- lpr client
This is a cool program written by David Burgess to print
files using the Line Printer Daemon protocol, widely used in
Unix machines. It uses the lp.lua implementation, in the
etc directory. Just run 'lua lpr.lua <filename>
queue=<printername>' and the file will print!
cddb.lua -- CDDB client
This is the first try on a simple CDDB client. Not really
useful, but one day it might become a module.
daytimeclnt.lua -- day time client
Just run the program to retrieve the hour and date in
readable form from any server running an UDP daytime daemon.
echoclnt.lua -- UDP echo client
echosrvr.lua -- UDP echo server
These are a UDP echo client/server pair. They work with
other client and servers as well.
tinyirc.lua -- irc like broadcast server
This is a simple server that waits simultaneously on two
server sockets for telnet connections. Everything it
receives from the telnet clients is broadcasted to every
other connected client. It tests the select function and
shows how to create a simple server whith LuaSocket. Just
run tinyirc.lua and then open as many telnet connections
as you want to ports 8080 and 8081.
Good luck,
Diego.

46
samples/cddb.lua Normal file
View File

@ -0,0 +1,46 @@
local socket = require("socket")
local http = require("socket.http")
if not arg or not arg[1] or not arg[2] then
print("luasocket cddb.lua <category> <disc-id> [<server>]")
os.exit(1)
end
local server = arg[3] or "http://freedb.freedb.org/~cddb/cddb.cgi"
function parse(body)
local lines = string.gfind(body, "(.-)\r\n")
local status = lines()
local code, message = socket.skip(2, string.find(status, "(%d%d%d) (.*)"))
if tonumber(code) ~= 210 then
return nil, code, message
end
local data = {}
for l in lines do
local c = string.sub(l, 1, 1)
if c ~= '#' and c ~= '.' then
local key, value = socket.skip(2, string.find(l, "(.-)=(.*)"))
value = string.gsub(value, "\\n", "\n")
value = string.gsub(value, "\\\\", "\\")
value = string.gsub(value, "\\t", "\t")
data[key] = value
end
end
return data, code, message
end
local host = socket.dns.gethostname()
local query = "%s?cmd=cddb+read+%s+%s&hello=LuaSocket+%s+LuaSocket+2.0&proto=6"
local url = string.format(query, server, arg[1], arg[2], host)
local body, headers, code = http.request(url)
if code == 200 then
local data, code, error = parse(body)
if not data then
print(error or code)
else
for i,v in pairs(data) do
io.write(i, ': ', v, '\n')
end
end
else print(error) end

23
samples/daytimeclnt.lua Normal file
View File

@ -0,0 +1,23 @@
-----------------------------------------------------------------------------
-- UDP sample: daytime protocol client
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id: daytimeclnt.lua,v 1.11 2004/06/21 06:07:57 diego Exp $
-----------------------------------------------------------------------------
local socket = require"socket"
host = host or "127.0.0.1"
port = port or 13
if arg then
host = arg[1] or host
port = arg[2] or port
end
host = socket.dns.toip(host)
udp = socket.udp()
print("Using host '" ..host.. "' and port " ..port.. "...")
udp:setpeername(host, port)
udp:settimeout(3)
sent, err = udp:send("anything")
if err then print(err) os.exit() end
dgram, err = udp:receive()
if not dgram then print(err) os.exit() end
io.write(dgram)

24
samples/echoclnt.lua Normal file
View File

@ -0,0 +1,24 @@
-----------------------------------------------------------------------------
-- UDP sample: echo protocol client
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id: echoclnt.lua,v 1.10 2005/01/02 22:44:00 diego Exp $
-----------------------------------------------------------------------------
local socket = require("socket")
host = host or "localhost"
port = port or 7
if arg then
host = arg[1] or host
port = arg[2] or port
end
host = socket.dns.toip(host)
udp = assert(socket.udp())
assert(udp:setpeername(host, port))
print("Using remote host '" ..host.. "' and port " .. port .. "...")
while 1 do
line = io.read()
if not line or line == "" then os.exit() end
assert(udp:send(line))
dgram = assert(udp:receive())
print(dgram)
end

29
samples/echosrvr.lua Normal file
View File

@ -0,0 +1,29 @@
-----------------------------------------------------------------------------
-- UDP sample: echo protocol server
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id: echosrvr.lua,v 1.12 2005/11/22 08:33:29 diego Exp $
-----------------------------------------------------------------------------
local socket = require("socket")
host = host or "127.0.0.1"
port = port or 7
if arg then
host = arg[1] or host
port = arg[2] or port
end
print("Binding to host '" ..host.. "' and port " ..port.. "...")
udp = assert(socket.udp())
assert(udp:setsockname(host, port))
assert(udp:settimeout(5))
ip, port = udp:getsockname()
assert(ip, port)
print("Waiting packets on " .. ip .. ":" .. port .. "...")
while 1 do
dgram, ip, port = udp:receivefrom()
if dgram then
print("Echoing '" .. dgram .. "' to " .. ip .. ":" .. port)
udp:sendto(dgram, ip, port)
else
print(ip)
end
end

26
samples/listener.lua Normal file
View File

@ -0,0 +1,26 @@
-----------------------------------------------------------------------------
-- TCP sample: Little program to dump lines received at a given port
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id: listener.lua,v 1.11 2005/01/02 22:44:00 diego Exp $
-----------------------------------------------------------------------------
local socket = require("socket")
host = host or "*"
port = port or 8080
if arg then
host = arg[1] or host
port = arg[2] or port
end
print("Binding to host '" ..host.. "' and port " ..port.. "...")
s = assert(socket.bind(host, port))
i, p = s:getsockname()
assert(i, p)
print("Waiting connection from talker on " .. i .. ":" .. p .. "...")
c = assert(s:accept())
print("Connected. Here is the stuff:")
l, e = c:receive()
while not e do
print(l)
l, e = c:receive()
end
print(e)

51
samples/lpr.lua Normal file
View File

@ -0,0 +1,51 @@
local lp = require("socket.lp")
local function usage()
print('\nUsage: lua lpr.lua [filename] [keyword=val...]\n')
print('Valid keywords are :')
print(
' host=remote host or IP address (default "localhost")\n' ..
' queue=remote queue or printer name (default "printer")\n' ..
' port=remote port number (default 515)\n' ..
' user=sending user name\n' ..
' format=["binary" | "text" | "ps" | "pr" | "fortran"] (default "binary")\n' ..
' banner=true|false\n' ..
' indent=number of columns to indent\n' ..
' mail=email of address to notify when print is complete\n' ..
' title=title to use for "pr" format\n' ..
' width=width for "text" or "pr" formats\n' ..
' class=\n' ..
' job=\n' ..
' name=\n' ..
' localbind=true|false\n'
)
return nil
end
if not arg or not arg[1] then
return usage()
end
do
local opt = {}
local pat = "[%s%c%p]*([%w]*)=([\"]?[%w%s_!@#$%%^&*()<>:;]+[\"]\?\.?)"
for i = 2, table.getn(arg), 1 do
string.gsub(arg[i], pat, function(name, value) opt[name] = value end)
end
if not arg[2] then
return usage()
end
if arg[1] ~= "query" then
opt.file = arg[1]
r,e=lp.send(opt)
io.stdout:write(tostring(r or e),'\n')
else
r,e=lp.query(opt)
io.stdout:write(tostring(r or e), '\n')
end
end
-- trivial tests
--lua lp.lua lp.lua queue=default host=localhost
--lua lp.lua lp.lua queue=default host=localhost format=binary localbind=1
--lua lp.lua query queue=default host=localhost

18
samples/mclisten.lua Normal file
View File

@ -0,0 +1,18 @@
local socket = require"socket"
local group = "225.0.0.37"
local port = 12345
local c = assert(socket.udp())
print(assert(c:setoption("reuseport", true)))
print(assert(c:setsockname("*", port)))
--print("loop:", c:getoption("ip-multicast-loop"))
--print(assert(c:setoption("ip-multicast-loop", false)))
--print("loop:", c:getoption("ip-multicast-loop"))
--print("if:", c:getoption("ip-multicast-if"))
--print(assert(c:setoption("ip-multicast-if", "127.0.0.1")))
--print("if:", c:getoption("ip-multicast-if"))
--print(assert(c:setoption("ip-multicast-if", "10.0.1.4")))
--print("if:", c:getoption("ip-multicast-if"))
print(assert(c:setoption("ip-add-membership", {multiaddr = group, interface = "*"})))
while 1 do
print(c:receivefrom())
end

20
samples/mcsend.lua Normal file
View File

@ -0,0 +1,20 @@
local socket = require"socket"
local group = "225.0.0.37"
local port = 12345
local c = assert(socket.udp())
--print(assert(c:setoption("reuseport", true)))
--print(assert(c:setsockname("*", port)))
--print(assert(c:setoption("ip-multicast-loop", false)))
--print(assert(c:setoption("ip-multicast-ttl", 4)))
--print(assert(c:setoption("ip-multicast-if", "10.0.1.3")))
--print(assert(c:setoption("ip-add-membership", {multiaddr = group, interface = "*"})))
local i = 0
while 1 do
local message = string.format("hello all %d!", i)
assert(c:sendto(message, group, port))
print("sent " .. message)
socket.sleep(1)
c:settimeout(0.5)
print(c:receivefrom())
i = i + 1
end

21
samples/talker.lua Normal file
View File

@ -0,0 +1,21 @@
-----------------------------------------------------------------------------
-- TCP sample: Little program to send text lines to a given host/port
-- LuaSocket sample files
-- Author: Diego Nehab
-- RCS ID: $Id: talker.lua,v 1.9 2005/01/02 22:44:00 diego Exp $
-----------------------------------------------------------------------------
local socket = require("socket")
host = host or "localhost"
port = port or 8080
if arg then
host = arg[1] or host
port = arg[2] or port
end
print("Attempting connection to host '" ..host.. "' and port " ..port.. "...")
c = assert(socket.connect(host, port))
print("Connected! Please type stuff (empty line to stop):")
l = io.read()
while l and l ~= "" and not e do
assert(c:send(l .. "\n"))
l = io.read()
end

90
samples/tinyirc.lua Normal file
View File

@ -0,0 +1,90 @@
-----------------------------------------------------------------------------
-- Select sample: simple text line server
-- LuaSocket sample files.
-- Author: Diego Nehab
-- RCS ID: $Id: tinyirc.lua,v 1.14 2005/11/22 08:33:29 diego Exp $
-----------------------------------------------------------------------------
local socket = require("socket")
host = host or "*"
port1 = port1 or 8080
port2 = port2 or 8181
if arg then
host = arg[1] or host
port1 = arg[2] or port1
port2 = arg[3] or port2
end
server1 = assert(socket.bind(host, port1))
server2 = assert(socket.bind(host, port2))
server1:settimeout(1) -- make sure we don't block in accept
server2:settimeout(1)
io.write("Servers bound\n")
-- simple set implementation
-- the select function doesn't care about what is passed to it as long as
-- it behaves like a table
-- creates a new set data structure
function newset()
local reverse = {}
local set = {}
return setmetatable(set, {__index = {
insert = function(set, value)
if not reverse[value] then
table.insert(set, value)
reverse[value] = table.getn(set)
end
end,
remove = function(set, value)
local index = reverse[value]
if index then
reverse[value] = nil
local top = table.remove(set)
if top ~= value then
reverse[top] = index
set[index] = top
end
end
end
}})
end
set = newset()
io.write("Inserting servers in set\n")
set:insert(server1)
set:insert(server2)
while 1 do
local readable, _, error = socket.select(set, nil)
for _, input in ipairs(readable) do
-- is it a server socket?
if input == server1 or input == server2 then
io.write("Waiting for clients\n")
local new = input:accept()
if new then
new:settimeout(1)
io.write("Inserting client in set\n")
set:insert(new)
end
-- it is a client socket
else
local line, error = input:receive()
if error then
input:close()
io.write("Removing client from set\n")
set:remove(input)
else
io.write("Broadcasting line '", line, "'\n")
writable, error = socket.skip(1, socket.select(nil, set, 1))
if not error then
for __, output in ipairs(writable) do
if output ~= input then
output:send(line .. "\n")
end
end
else io.write("No client ready to receive!!!\n") end
end
end
end
end

259
socket.vcproj Normal file
View File

@ -0,0 +1,259 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="socket"
ProjectGUID="{66E3CE14-884D-4AEA-9F20-15A0BEAF8C5A}"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="src"
IntermediateDirectory="src"
ConfigurationType="2"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\lua-5.1.4\src"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LUASOCKET_EXPORTS;LUASOCKET_API=__declspec(dllexport);_CRT_SECURE_NO_WARNINGS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib lua5.1.lib"
OutputFile="$(OutDir)/socket.dll"
LinkIncremental="2"
AdditionalLibraryDirectories="..\lua-5.1.4"
GenerateDebugInformation="true"
ProgramDatabaseFile="$(OutDir)/socket.pdb"
SubSystem="2"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
ImportLibrary="$(OutDir)/socket.lib"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="./src"
IntermediateDirectory="./src"
ConfigurationType="2"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\lua-5.1.4\src"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LUASOCKET_EXPORTS;LUASOCKET_API=__declspec(dllexport); LUASOCKET_DEBUG; _CRT_SECURE_NO_WARNINGS"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="0"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib lua5.1.lib"
OutputFile="$(OutDir)/socket.dll"
LinkIncremental="1"
AdditionalLibraryDirectories="..\lua-5.1.4"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
ImportLibrary="$(OutDir)/socket.lib"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="src\auxiliar.c"
>
</File>
<File
RelativePath="src\buffer.c"
>
</File>
<File
RelativePath="src\except.c"
>
</File>
<File
RelativePath="src\inet.c"
>
</File>
<File
RelativePath="src\io.c"
>
</File>
<File
RelativePath="src\luasocket.c"
>
</File>
<File
RelativePath="src\options.c"
>
</File>
<File
RelativePath="src\select.c"
>
</File>
<File
RelativePath="src\tcp.c"
>
</File>
<File
RelativePath="src\timeout.c"
>
</File>
<File
RelativePath="src\udp.c"
>
</File>
<File
RelativePath="src\wsocket.c"
>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
GeneratePreprocessedFile="0"
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

150
src/auxiliar.c Normal file
View File

@ -0,0 +1,150 @@
/*=========================================================================*\
* Auxiliar routines for class hierarchy manipulation
* LuaSocket toolkit
*
* RCS ID: $Id: auxiliar.c,v 1.14 2005/10/07 04:40:59 diego Exp $
\*=========================================================================*/
#include <string.h>
#include <stdio.h>
#include "auxiliar.h"
#include "lua_typeerror.h"
/*=========================================================================*\
* Exported functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Initializes the module
\*-------------------------------------------------------------------------*/
int auxiliar_open(lua_State *L) {
(void) L;
return 0;
}
/*-------------------------------------------------------------------------*\
* Creates a new class with given methods
* Methods whose names start with __ are passed directly to the metatable.
\*-------------------------------------------------------------------------*/
void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func) {
luaL_newmetatable(L, classname); /* mt */
/* create __index table to place methods */
lua_pushstring(L, "__index"); /* mt,"__index" */
lua_newtable(L); /* mt,"__index",it */
/* put class name into class metatable */
lua_pushstring(L, "class"); /* mt,"__index",it,"class" */
lua_pushstring(L, classname); /* mt,"__index",it,"class",classname */
lua_rawset(L, -3); /* mt,"__index",it */
/* pass all methods that start with _ to the metatable, and all others
* to the index table */
for (; func->name; func++) { /* mt,"__index",it */
lua_pushstring(L, func->name);
lua_pushcfunction(L, func->func);
lua_rawset(L, func->name[0] == '_' ? -5: -3);
}
lua_rawset(L, -3); /* mt */
lua_pop(L, 1);
}
/*-------------------------------------------------------------------------*\
* Prints the value of a class in a nice way
\*-------------------------------------------------------------------------*/
int auxiliar_tostring(lua_State *L) {
char buf[32];
if (!lua_getmetatable(L, 1)) goto error;
lua_pushstring(L, "__index");
lua_gettable(L, -2);
if (!lua_istable(L, -1)) goto error;
lua_pushstring(L, "class");
lua_gettable(L, -2);
if (!lua_isstring(L, -1)) goto error;
sprintf(buf, "%p", lua_touserdata(L, 1));
lua_pushfstring(L, "%s: %s", lua_tostring(L, -1), buf);
return 1;
error:
lua_pushstring(L, "invalid object passed to 'auxiliar.c:__tostring'");
lua_error(L);
return 1;
}
/*-------------------------------------------------------------------------*\
* Insert class into group
\*-------------------------------------------------------------------------*/
void auxiliar_add2group(lua_State *L, const char *classname, const char *groupname) {
luaL_getmetatable(L, classname);
lua_pushstring(L, groupname);
lua_pushboolean(L, 1);
lua_rawset(L, -3);
lua_pop(L, 1);
}
/*-------------------------------------------------------------------------*\
* Make sure argument is a boolean
\*-------------------------------------------------------------------------*/
int auxiliar_checkboolean(lua_State *L, int objidx) {
if (!lua_isboolean(L, objidx))
luaL_typeerror(L, objidx, lua_typename(L, LUA_TBOOLEAN));
return lua_toboolean(L, objidx);
}
/*-------------------------------------------------------------------------*\
* Return userdata pointer if object belongs to a given class, abort with
* error otherwise
\*-------------------------------------------------------------------------*/
void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx) {
void *data = auxiliar_getclassudata(L, classname, objidx);
if (!data) {
char msg[45];
sprintf(msg, "%.35s expected", classname);
luaL_argerror(L, objidx, msg);
}
return data;
}
/*-------------------------------------------------------------------------*\
* Return userdata pointer if object belongs to a given group, abort with
* error otherwise
\*-------------------------------------------------------------------------*/
void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx) {
void *data = auxiliar_getgroupudata(L, groupname, objidx);
if (!data) {
char msg[45];
sprintf(msg, "%.35s expected", groupname);
luaL_argerror(L, objidx, msg);
}
return data;
}
/*-------------------------------------------------------------------------*\
* Set object class
\*-------------------------------------------------------------------------*/
void auxiliar_setclass(lua_State *L, const char *classname, int objidx) {
luaL_getmetatable(L, classname);
if (objidx < 0) objidx--;
lua_setmetatable(L, objidx);
}
/*-------------------------------------------------------------------------*\
* Get a userdata pointer if object belongs to a given group. Return NULL
* otherwise
\*-------------------------------------------------------------------------*/
void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx) {
if (!lua_getmetatable(L, objidx))
return NULL;
lua_pushstring(L, groupname);
lua_rawget(L, -2);
if (lua_isnil(L, -1)) {
lua_pop(L, 2);
return NULL;
} else {
lua_pop(L, 2);
return lua_touserdata(L, objidx);
}
}
/*-------------------------------------------------------------------------*\
* Get a userdata pointer if object belongs to a given class. Return NULL
* otherwise
\*-------------------------------------------------------------------------*/
void *auxiliar_getclassudata(lua_State *L, const char *classname, int objidx) {
return luaL_checkudata(L, objidx, classname);
}

46
src/auxiliar.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef AUXILIAR_H
#define AUXILIAR_H
/*=========================================================================*\
* Auxiliar routines for class hierarchy manipulation
* LuaSocket toolkit (but completely independent of other LuaSocket modules)
*
* A LuaSocket class is a name associated with Lua metatables. A LuaSocket
* group is a name associated with a class. A class can belong to any number
* of groups. This module provides the functionality to:
*
* - create new classes
* - add classes to groups
* - set the class of objects
* - check if an object belongs to a given class or group
* - get the userdata associated to objects
* - print objects in a pretty way
*
* LuaSocket class names follow the convention <module>{<class>}. Modules
* can define any number of classes and groups. The module tcp.c, for
* example, defines the classes tcp{master}, tcp{client} and tcp{server} and
* the groups tcp{client,server} and tcp{any}. Module functions can then
* perform type-checking on their arguments by either class or group.
*
* LuaSocket metatables define the __index metamethod as being a table. This
* table has one field for each method supported by the class, and a field
* "class" with the class name.
*
* The mapping from class name to the corresponding metatable and the
* reverse mapping are done using lauxlib.
\*=========================================================================*/
#include "lua.h"
#include "lauxlib.h"
int auxiliar_open(lua_State *L);
void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func);
void auxiliar_add2group(lua_State *L, const char *classname, const char *group);
void auxiliar_setclass(lua_State *L, const char *classname, int objidx);
void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx);
void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx);
void *auxiliar_getclassudata(lua_State *L, const char *groupname, int objidx);
void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx);
int auxiliar_checkboolean(lua_State *L, int objidx);
int auxiliar_tostring(lua_State *L);
#endif /* AUXILIAR_H */

BIN
src/auxiliar.o Normal file

Binary file not shown.

278
src/buffer.c Normal file
View File

@ -0,0 +1,278 @@
/*=========================================================================*\
* Input/Output interface for Lua programs
* LuaSocket toolkit
*
* RCS ID: $Id: buffer.c,v 1.29 2009/05/27 09:31:35 diego Exp $
\*=========================================================================*/
#include "lua.h"
#include "lauxlib.h"
#include "buffer.h"
/*=========================================================================*\
* Internal function prototypes
\*=========================================================================*/
static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b);
static int recvline(p_buffer buf, luaL_Buffer *b);
static int recvall(p_buffer buf, luaL_Buffer *b);
static int buffer_get(p_buffer buf, const char **data, size_t *count);
static void buffer_skip(p_buffer buf, size_t count);
static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent);
/* min and max macros */
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? x : y)
#endif
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? x : y)
#endif
/*=========================================================================*\
* Exported functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int buffer_open(lua_State *L) {
(void) L;
return 0;
}
/*-------------------------------------------------------------------------*\
* Initializes C structure
\*-------------------------------------------------------------------------*/
void buffer_init(p_buffer buf, p_io io, p_timeout tm) {
buf->first = buf->last = 0;
buf->io = io;
buf->tm = tm;
buf->received = buf->sent = 0;
buf->birthday = timeout_gettime();
}
/*-------------------------------------------------------------------------*\
* object:getstats() interface
\*-------------------------------------------------------------------------*/
int buffer_meth_getstats(lua_State *L, p_buffer buf) {
lua_pushnumber(L, buf->received);
lua_pushnumber(L, buf->sent);
lua_pushnumber(L, timeout_gettime() - buf->birthday);
return 3;
}
/*-------------------------------------------------------------------------*\
* object:setstats() interface
\*-------------------------------------------------------------------------*/
int buffer_meth_setstats(lua_State *L, p_buffer buf) {
buf->received = (long) luaL_optnumber(L, 2, buf->received);
buf->sent = (long) luaL_optnumber(L, 3, buf->sent);
if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* object:send() interface
\*-------------------------------------------------------------------------*/
int buffer_meth_send(lua_State *L, p_buffer buf) {
int top = lua_gettop(L);
int err = IO_DONE;
size_t size = 0, sent = 0;
const char *data = luaL_checklstring(L, 2, &size);
long start = (long) luaL_optnumber(L, 3, 1);
long end = (long) luaL_optnumber(L, 4, -1);
#ifdef LUASOCKET_DEBUG
p_timeout tm = timeout_markstart(buf->tm);
#endif
if (start < 0) start = (long) (size+start+1);
if (end < 0) end = (long) (size+end+1);
if (start < 1) start = (long) 1;
if (end > (long) size) end = (long) size;
if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent);
/* check if there was an error */
if (err != IO_DONE) {
lua_pushnil(L);
lua_pushstring(L, buf->io->error(buf->io->ctx, err));
lua_pushnumber(L, sent+start-1);
} else {
lua_pushnumber(L, sent+start-1);
lua_pushnil(L);
lua_pushnil(L);
}
#ifdef LUASOCKET_DEBUG
/* push time elapsed during operation as the last return value */
lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm));
#endif
return lua_gettop(L) - top;
}
/*-------------------------------------------------------------------------*\
* object:receive() interface
\*-------------------------------------------------------------------------*/
int buffer_meth_receive(lua_State *L, p_buffer buf) {
int err = IO_DONE, top = lua_gettop(L);
luaL_Buffer b;
size_t size;
const char *part = luaL_optlstring(L, 3, "", &size);
#ifdef LUASOCKET_DEBUG
p_timeout tm = timeout_markstart(buf->tm);
#endif
/* initialize buffer with optional extra prefix
* (useful for concatenating previous partial results) */
luaL_buffinit(L, &b);
luaL_addlstring(&b, part, size);
/* receive new patterns */
if (!lua_isnumber(L, 2)) {
const char *p= luaL_optstring(L, 2, "*l");
if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);
else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b);
else luaL_argcheck(L, 0, 2, "invalid receive pattern");
/* get a fixed number of bytes (minus what was already partially
* received) */
} else {
double n = lua_tonumber(L, 2);
size_t wanted = (size_t) n;
luaL_argcheck(L, n >= 0, 2, "invalid receive pattern");
if (size == 0 || wanted > size)
err = recvraw(buf, wanted-size, &b);
}
/* check if there was an error */
if (err != IO_DONE) {
/* we can't push anyting in the stack before pushing the
* contents of the buffer. this is the reason for the complication */
luaL_pushresult(&b);
lua_pushstring(L, buf->io->error(buf->io->ctx, err));
lua_pushvalue(L, -2);
lua_pushnil(L);
lua_replace(L, -4);
} else {
luaL_pushresult(&b);
lua_pushnil(L);
lua_pushnil(L);
}
#ifdef LUASOCKET_DEBUG
/* push time elapsed during operation as the last return value */
lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm));
#endif
return lua_gettop(L) - top;
}
/*-------------------------------------------------------------------------*\
* Determines if there is any data in the read buffer
\*-------------------------------------------------------------------------*/
int buffer_isempty(p_buffer buf) {
return buf->first >= buf->last;
}
/*=========================================================================*\
* Internal functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Sends a block of data (unbuffered)
\*-------------------------------------------------------------------------*/
#define STEPSIZE 8192
static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) {
p_io io = buf->io;
p_timeout tm = buf->tm;
size_t total = 0;
int err = IO_DONE;
while (total < count && err == IO_DONE) {
size_t done = 0;
size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE;
err = io->send(io->ctx, data+total, step, &done, tm);
total += done;
}
*sent = total;
buf->sent += total;
return err;
}
/*-------------------------------------------------------------------------*\
* Reads a fixed number of bytes (buffered)
\*-------------------------------------------------------------------------*/
static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) {
int err = IO_DONE;
size_t total = 0;
while (err == IO_DONE) {
size_t count; const char *data;
err = buffer_get(buf, &data, &count);
count = MIN(count, wanted - total);
luaL_addlstring(b, data, count);
buffer_skip(buf, count);
total += count;
if (total >= wanted) break;
}
return err;
}
/*-------------------------------------------------------------------------*\
* Reads everything until the connection is closed (buffered)
\*-------------------------------------------------------------------------*/
static int recvall(p_buffer buf, luaL_Buffer *b) {
int err = IO_DONE;
size_t total = 0;
while (err == IO_DONE) {
const char *data; size_t count;
err = buffer_get(buf, &data, &count);
total += count;
luaL_addlstring(b, data, count);
buffer_skip(buf, count);
}
if (err == IO_CLOSED) {
if (total > 0) return IO_DONE;
else return IO_CLOSED;
} else return err;
}
/*-------------------------------------------------------------------------*\
* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF
* are not returned by the function and are discarded from the buffer
\*-------------------------------------------------------------------------*/
static int recvline(p_buffer buf, luaL_Buffer *b) {
int err = IO_DONE;
while (err == IO_DONE) {
size_t count, pos; const char *data;
err = buffer_get(buf, &data, &count);
pos = 0;
while (pos < count && data[pos] != '\n') {
/* we ignore all \r's */
if (data[pos] != '\r') luaL_addchar(b, data[pos]);
pos++;
}
if (pos < count) { /* found '\n' */
buffer_skip(buf, pos+1); /* skip '\n' too */
break; /* we are done */
} else /* reached the end of the buffer */
buffer_skip(buf, pos);
}
return err;
}
/*-------------------------------------------------------------------------*\
* Skips a given number of bytes from read buffer. No data is read from the
* transport layer
\*-------------------------------------------------------------------------*/
static void buffer_skip(p_buffer buf, size_t count) {
buf->received += count;
buf->first += count;
if (buffer_isempty(buf))
buf->first = buf->last = 0;
}
/*-------------------------------------------------------------------------*\
* Return any data available in buffer, or get more data from transport layer
* if buffer is empty
\*-------------------------------------------------------------------------*/
static int buffer_get(p_buffer buf, const char **data, size_t *count) {
int err = IO_DONE;
p_io io = buf->io;
p_timeout tm = buf->tm;
if (buffer_isempty(buf)) {
size_t got;
err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm);
buf->first = 0;
buf->last = got;
}
*count = buf->last - buf->first;
*data = buf->data + buf->first;
return err;
}

45
src/buffer.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef BUF_H
#define BUF_H
/*=========================================================================*\
* Input/Output interface for Lua programs
* LuaSocket toolkit
*
* Line patterns require buffering. Reading one character at a time involves
* too many system calls and is very slow. This module implements the
* LuaSocket interface for input/output on connected objects, as seen by
* Lua programs.
*
* Input is buffered. Output is *not* buffered because there was no simple
* way of making sure the buffered output data would ever be sent.
*
* The module is built on top of the I/O abstraction defined in io.h and the
* timeout management is done with the timeout.h interface.
\*=========================================================================*/
#include "lua.h"
#include "io.h"
#include "timeout.h"
/* buffer size in bytes */
#define BUF_SIZE 8192
/* buffer control structure */
typedef struct t_buffer_ {
double birthday; /* throttle support info: creation time, */
size_t sent, received; /* bytes sent, and bytes received */
p_io io; /* IO driver used for this buffer */
p_timeout tm; /* timeout management for this buffer */
size_t first, last; /* index of first and last bytes of stored data */
char data[BUF_SIZE]; /* storage space for buffer data */
} t_buffer;
typedef t_buffer *p_buffer;
int buffer_open(lua_State *L);
void buffer_init(p_buffer buf, p_io io, p_timeout tm);
int buffer_meth_send(lua_State *L, p_buffer buf);
int buffer_meth_receive(lua_State *L, p_buffer buf);
int buffer_meth_getstats(lua_State *L, p_buffer buf);
int buffer_meth_setstats(lua_State *L, p_buffer buf);
int buffer_isempty(p_buffer buf);
#endif /* BUF_H */

BIN
src/buffer.o Normal file

Binary file not shown.

99
src/except.c Normal file
View File

@ -0,0 +1,99 @@
/*=========================================================================*\
* Simple exception support
* LuaSocket toolkit
*
* RCS ID: $Id: except.c,v 1.8 2005/09/29 06:11:41 diego Exp $
\*=========================================================================*/
#include <stdio.h>
#include "lua.h"
#include "lauxlib.h"
#include "except.h"
/*=========================================================================*\
* Internal function prototypes.
\*=========================================================================*/
static int global_protect(lua_State *L);
static int global_newtry(lua_State *L);
static int protected_(lua_State *L);
static int finalize(lua_State *L);
static int do_nothing(lua_State *L);
/* except functions */
static luaL_Reg func[] = {
{"newtry", global_newtry},
{"protect", global_protect},
{NULL, NULL}
};
/*-------------------------------------------------------------------------*\
* Try factory
\*-------------------------------------------------------------------------*/
static void wrap(lua_State *L) {
lua_newtable(L);
lua_pushnumber(L, 1);
lua_pushvalue(L, -3);
lua_settable(L, -3);
lua_insert(L, -2);
lua_pop(L, 1);
}
static int finalize(lua_State *L) {
if (!lua_toboolean(L, 1)) {
lua_pushvalue(L, lua_upvalueindex(1));
lua_pcall(L, 0, 0, 0);
lua_settop(L, 2);
wrap(L);
lua_error(L);
return 0;
} else return lua_gettop(L);
}
static int do_nothing(lua_State *L) {
(void) L;
return 0;
}
static int global_newtry(lua_State *L) {
lua_settop(L, 1);
if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing);
lua_pushcclosure(L, finalize, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Protect factory
\*-------------------------------------------------------------------------*/
static int unwrap(lua_State *L) {
if (lua_istable(L, -1)) {
lua_pushnumber(L, 1);
lua_gettable(L, -2);
lua_pushnil(L);
lua_insert(L, -2);
return 1;
} else return 0;
}
static int protected_(lua_State *L) {
lua_pushvalue(L, lua_upvalueindex(1));
lua_insert(L, 1);
if (lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) != 0) {
if (unwrap(L)) return 2;
else lua_error(L);
return 0;
} else return lua_gettop(L);
}
static int global_protect(lua_State *L) {
lua_pushcclosure(L, protected_, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Init module
\*-------------------------------------------------------------------------*/
int except_open(lua_State *L) {
luaL_openlib(L, NULL, func, 0);
return 0;
}

33
src/except.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef EXCEPT_H
#define EXCEPT_H
/*=========================================================================*\
* Exception control
* LuaSocket toolkit (but completely independent from other modules)
*
* This provides support for simple exceptions in Lua. During the
* development of the HTTP/FTP/SMTP support, it became aparent that
* error checking was taking a substantial amount of the coding. These
* function greatly simplify the task of checking errors.
*
* The main idea is that functions should return nil as its first return
* value when it finds an error, and return an error message (or value)
* following nil. In case of success, as long as the first value is not nil,
* the other values don't matter.
*
* The idea is to nest function calls with the "try" function. This function
* checks the first value, and calls "error" on the second if the first is
* nil. Otherwise, it returns all values it received.
*
* The protect function returns a new function that behaves exactly like the
* function it receives, but the new function doesn't throw exceptions: it
* returns nil followed by the error message instead.
*
* With these two function, it's easy to write functions that throw
* exceptions on error, but that don't interrupt the user script.
\*=========================================================================*/
#include "lua.h"
int except_open(lua_State *L);
#endif

BIN
src/except.o Normal file

Binary file not shown.

281
src/ftp.lua Normal file
View File

@ -0,0 +1,281 @@
-----------------------------------------------------------------------------
-- FTP support for the Lua language
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: ftp.lua,v 1.45 2007/07/11 19:25:47 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local table = require("table")
local string = require("string")
local math = require("math")
local socket = require("socket")
local url = require("socket.url")
local tp = require("socket.tp")
local ltn12 = require("ltn12")
module("socket.ftp")
-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
-- timeout in seconds before the program gives up on a connection
TIMEOUT = 60
-- default port for ftp service
PORT = 21
-- this is the default anonymous password. used when no password is
-- provided in url. should be changed to your e-mail.
USER = "ftp"
PASSWORD = "anonymous@anonymous.org"
-----------------------------------------------------------------------------
-- Low level FTP API
-----------------------------------------------------------------------------
local metat = { __index = {} }
function open(server, port, create)
local tp = socket.try(tp.connect(server, port or PORT, TIMEOUT, create))
local f = base.setmetatable({ tp = tp }, metat)
-- make sure everything gets closed in an exception
f.try = socket.newtry(function() f:close() end)
return f
end
function metat.__index:portconnect()
self.try(self.server:settimeout(TIMEOUT))
self.data = self.try(self.server:accept())
self.try(self.data:settimeout(TIMEOUT))
end
function metat.__index:pasvconnect()
self.data = self.try(socket.tcp())
self.try(self.data:settimeout(TIMEOUT))
self.try(self.data:connect(self.pasvt.ip, self.pasvt.port))
end
function metat.__index:login(user, password)
self.try(self.tp:command("user", user or USER))
local code, reply = self.try(self.tp:check{"2..", 331})
if code == 331 then
self.try(self.tp:command("pass", password or PASSWORD))
self.try(self.tp:check("2.."))
end
return 1
end
function metat.__index:pasv()
self.try(self.tp:command("pasv"))
local code, reply = self.try(self.tp:check("2.."))
local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)"
local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern))
self.try(a and b and c and d and p1 and p2, reply)
self.pasvt = {
ip = string.format("%d.%d.%d.%d", a, b, c, d),
port = p1*256 + p2
}
if self.server then
self.server:close()
self.server = nil
end
return self.pasvt.ip, self.pasvt.port
end
function metat.__index:port(ip, port)
self.pasvt = nil
if not ip then
ip, port = self.try(self.tp:getcontrol():getsockname())
self.server = self.try(socket.bind(ip, 0))
ip, port = self.try(self.server:getsockname())
self.try(self.server:settimeout(TIMEOUT))
end
local pl = math.mod(port, 256)
local ph = (port - pl)/256
local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",")
self.try(self.tp:command("port", arg))
self.try(self.tp:check("2.."))
return 1
end
function metat.__index:send(sendt)
self.try(self.pasvt or self.server, "need port or pasv first")
-- if there is a pasvt table, we already sent a PASV command
-- we just get the data connection into self.data
if self.pasvt then self:pasvconnect() end
-- get the transfer argument and command
local argument = sendt.argument or
url.unescape(string.gsub(sendt.path or "", "^[/\\]", ""))
if argument == "" then argument = nil end
local command = sendt.command or "stor"
-- send the transfer command and check the reply
self.try(self.tp:command(command, argument))
local code, reply = self.try(self.tp:check{"2..", "1.."})
-- if there is not a a pasvt table, then there is a server
-- and we already sent a PORT command
if not self.pasvt then self:portconnect() end
-- get the sink, source and step for the transfer
local step = sendt.step or ltn12.pump.step
local readt = {self.tp.c}
local checkstep = function(src, snk)
-- check status in control connection while downloading
local readyt = socket.select(readt, nil, 0)
if readyt[tp] then code = self.try(self.tp:check("2..")) end
return step(src, snk)
end
local sink = socket.sink("close-when-done", self.data)
-- transfer all data and check error
self.try(ltn12.pump.all(sendt.source, sink, checkstep))
if string.find(code, "1..") then self.try(self.tp:check("2..")) end
-- done with data connection
self.data:close()
-- find out how many bytes were sent
local sent = socket.skip(1, self.data:getstats())
self.data = nil
return sent
end
function metat.__index:receive(recvt)
self.try(self.pasvt or self.server, "need port or pasv first")
if self.pasvt then self:pasvconnect() end
local argument = recvt.argument or
url.unescape(string.gsub(recvt.path or "", "^[/\\]", ""))
if argument == "" then argument = nil end
local command = recvt.command or "retr"
self.try(self.tp:command(command, argument))
local code = self.try(self.tp:check{"1..", "2.."})
if not self.pasvt then self:portconnect() end
local source = socket.source("until-closed", self.data)
local step = recvt.step or ltn12.pump.step
self.try(ltn12.pump.all(source, recvt.sink, step))
if string.find(code, "1..") then self.try(self.tp:check("2..")) end
self.data:close()
self.data = nil
return 1
end
function metat.__index:cwd(dir)
self.try(self.tp:command("cwd", dir))
self.try(self.tp:check(250))
return 1
end
function metat.__index:type(type)
self.try(self.tp:command("type", type))
self.try(self.tp:check(200))
return 1
end
function metat.__index:greet()
local code = self.try(self.tp:check{"1..", "2.."})
if string.find(code, "1..") then self.try(self.tp:check("2..")) end
return 1
end
function metat.__index:quit()
self.try(self.tp:command("quit"))
self.try(self.tp:check("2.."))
return 1
end
function metat.__index:close()
if self.data then self.data:close() end
if self.server then self.server:close() end
return self.tp:close()
end
-----------------------------------------------------------------------------
-- High level FTP API
-----------------------------------------------------------------------------
local function override(t)
if t.url then
local u = url.parse(t.url)
for i,v in base.pairs(t) do
u[i] = v
end
return u
else return t end
end
local function tput(putt)
putt = override(putt)
socket.try(putt.host, "missing hostname")
local f = open(putt.host, putt.port, putt.create)
f:greet()
f:login(putt.user, putt.password)
if putt.type then f:type(putt.type) end
f:pasv()
local sent = f:send(putt)
f:quit()
f:close()
return sent
end
local default = {
path = "/",
scheme = "ftp"
}
local function parse(u)
local t = socket.try(url.parse(u, default))
socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'")
socket.try(t.host, "missing hostname")
local pat = "^type=(.)$"
if t.params then
t.type = socket.skip(2, string.find(t.params, pat))
socket.try(t.type == "a" or t.type == "i",
"invalid type '" .. t.type .. "'")
end
return t
end
local function sput(u, body)
local putt = parse(u)
putt.source = ltn12.source.string(body)
return tput(putt)
end
put = socket.protect(function(putt, body)
if base.type(putt) == "string" then return sput(putt, body)
else return tput(putt) end
end)
local function tget(gett)
gett = override(gett)
socket.try(gett.host, "missing hostname")
local f = open(gett.host, gett.port, gett.create)
f:greet()
f:login(gett.user, gett.password)
if gett.type then f:type(gett.type) end
f:pasv()
f:receive(gett)
f:quit()
return f:close()
end
local function sget(u)
local gett = parse(u)
local t = {}
gett.sink = ltn12.sink.table(t)
tget(gett)
return table.concat(t)
end
command = socket.protect(function(cmdt)
cmdt = override(cmdt)
socket.try(cmdt.host, "missing hostname")
socket.try(cmdt.command, "missing command")
local f = open(cmdt.host, cmdt.port, cmdt.create)
f:greet()
f:login(cmdt.user, cmdt.password)
f.try(f.tp:command(cmdt.command, cmdt.argument))
if cmdt.check then f.try(f.tp:check(cmdt.check)) end
f:quit()
return f:close()
end)
get = socket.protect(function(gett)
if base.type(gett) == "string" then return sget(gett)
else return tget(gett) end
end)

100
src/headers.lua Normal file
View File

@ -0,0 +1,100 @@
-----------------------------------------------------------------------------
-- Canonic header field capitalization
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id$
-----------------------------------------------------------------------------
module("socket.headers")
canonic = {
["accept"] = "Accept",
["accept-charset"] = "Accept-Charset",
["accept-encoding"] = "Accept-Encoding",
["accept-language"] = "Accept-Language",
["accept-ranges"] = "Accept-Ranges",
["action"] = "Action",
["alternate-recipient"] = "Alternate-Recipient",
["age"] = "Age",
["allow"] = "Allow",
["arrival-date"] = "Arrival-Date",
["authorization"] = "Authorization",
["bcc"] = "Bcc",
["cache-control"] = "Cache-Control",
["cc"] = "Cc",
["comments"] = "Comments",
["connection"] = "Connection",
["content-description"] = "Content-Description",
["content-disposition"] = "Content-Disposition",
["content-encoding"] = "Content-Encoding",
["content-id"] = "Content-ID",
["content-language"] = "Content-Language",
["content-length"] = "Content-Length",
["content-location"] = "Content-Location",
["content-md5"] = "Content-MD5",
["content-range"] = "Content-Range",
["content-transfer-encoding"] = "Content-Transfer-Encoding",
["content-type"] = "Content-Type",
["date"] = "Date",
["diagnostic-code"] = "Diagnostic-Code",
["dsn-gateway"] = "DSN-Gateway",
["etag"] = "ETag",
["expect"] = "Expect",
["expires"] = "Expires",
["final-log-id"] = "Final-Log-ID",
["final-recipient"] = "Final-Recipient",
["from"] = "From",
["host"] = "Host",
["if-match"] = "If-Match",
["if-modified-since"] = "If-Modified-Since",
["if-none-match"] = "If-None-Match",
["if-range"] = "If-Range",
["if-unmodified-since"] = "If-Unmodified-Since",
["in-reply-to"] = "In-Reply-To",
["keywords"] = "Keywords",
["last-attempt-date"] = "Last-Attempt-Date",
["last-modified"] = "Last-Modified",
["location"] = "Location",
["max-forwards"] = "Max-Forwards",
["message-id"] = "Message-ID",
["mime-version"] = "MIME-Version",
["original-envelope-id"] = "Original-Envelope-ID",
["original-recipient"] = "Original-Recipient",
["pragma"] = "Pragma",
["proxy-authenticate"] = "Proxy-Authenticate",
["proxy-authorization"] = "Proxy-Authorization",
["range"] = "Range",
["received"] = "Received",
["received-from-mta"] = "Received-From-MTA",
["references"] = "References",
["referer"] = "Referer",
["remote-mta"] = "Remote-MTA",
["reply-to"] = "Reply-To",
["reporting-mta"] = "Reporting-MTA",
["resent-bcc"] = "Resent-Bcc",
["resent-cc"] = "Resent-Cc",
["resent-date"] = "Resent-Date",
["resent-from"] = "Resent-From",
["resent-message-id"] = "Resent-Message-ID",
["resent-reply-to"] = "Resent-Reply-To",
["resent-sender"] = "Resent-Sender",
["resent-to"] = "Resent-To",
["retry-after"] = "Retry-After",
["return-path"] = "Return-Path",
["sender"] = "Sender",
["server"] = "Server",
["smtp-remote-recipient"] = "SMTP-Remote-Recipient",
["status"] = "Status",
["subject"] = "Subject",
["te"] = "TE",
["to"] = "To",
["trailer"] = "Trailer",
["transfer-encoding"] = "Transfer-Encoding",
["upgrade"] = "Upgrade",
["user-agent"] = "User-Agent",
["vary"] = "Vary",
["via"] = "Via",
["warning"] = "Warning",
["will-retry-until"] = "Will-Retry-Until",
["www-authenticate"] = "WWW-Authenticate",
["x-mailer"] = "X-Mailer",
}

352
src/http.lua Normal file
View File

@ -0,0 +1,352 @@
-----------------------------------------------------------------------------
-- HTTP/1.1 client support for the Lua language.
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: http.lua,v 1.72 2009/05/27 09:31:35 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-------------------------------------------------------------------------------
local socket = require("socket")
local url = require("socket.url")
local ltn12 = require("ltn12")
local mime = require("mime")
local string = require("string")
local headers = require("socket.headers")
local base = _G
local table = require("table")
module("socket.http")
-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
-- connection timeout in seconds
TIMEOUT = 60
-- default port for document retrieval
PORT = 80
-- user agent field sent in request
USERAGENT = socket._VERSION
-----------------------------------------------------------------------------
-- Reads MIME headers from a connection, unfolding where needed
-----------------------------------------------------------------------------
local function receiveheaders(sock, headers)
local line, name, value, err
headers = headers or {}
-- get first line
line, err = sock:receive()
if err then return nil, err end
-- headers go until a blank line is found
while line ~= "" do
-- get field-name and value
name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)"))
if not (name and value) then return nil, "malformed reponse headers" end
name = string.lower(name)
-- get next line (value might be folded)
line, err = sock:receive()
if err then return nil, err end
-- unfold any folded values
while string.find(line, "^%s") do
value = value .. line
line = sock:receive()
if err then return nil, err end
end
-- save pair in table
if headers[name] then headers[name] = headers[name] .. ", " .. value
else headers[name] = value end
end
return headers
end
-----------------------------------------------------------------------------
-- Extra sources and sinks
-----------------------------------------------------------------------------
socket.sourcet["http-chunked"] = function(sock, headers)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function()
-- get chunk size, skip extention
local line, err = sock:receive()
if err then return nil, err end
local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
if not size then return nil, "invalid chunk size" end
-- was it the last chunk?
if size > 0 then
-- if not, get chunk and skip terminating CRLF
local chunk, err, part = sock:receive(size)
if chunk then sock:receive() end
return chunk, err
else
-- if it was, read trailers into headers table
headers, err = receiveheaders(sock, headers)
if not headers then return nil, err end
end
end
})
end
socket.sinkt["http-chunked"] = function(sock)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function(self, chunk, err)
if not chunk then return sock:send("0\r\n\r\n") end
local size = string.format("%X\r\n", string.len(chunk))
return sock:send(size .. chunk .. "\r\n")
end
})
end
-----------------------------------------------------------------------------
-- Low level HTTP API
-----------------------------------------------------------------------------
local metat = { __index = {} }
function open(host, port, create)
-- create socket with user connect function, or with default
local c = socket.try((create or socket.tcp)())
local h = base.setmetatable({ c = c }, metat)
-- create finalized try
h.try = socket.newtry(function() h:close() end)
-- set timeout before connecting
h.try(c:settimeout(TIMEOUT))
h.try(c:connect(host, port or PORT))
-- here everything worked
return h
end
function metat.__index:sendrequestline(method, uri)
local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri)
return self.try(self.c:send(reqline))
end
function metat.__index:sendheaders(tosend)
local canonic = headers.canonic
local h = "\r\n"
for f, v in base.pairs(tosend) do
h = (canonic[f] or f) .. ": " .. v .. "\r\n" .. h
end
self.try(self.c:send(h))
return 1
end
function metat.__index:sendbody(headers, source, step)
source = source or ltn12.source.empty()
step = step or ltn12.pump.step
-- if we don't know the size in advance, send chunked and hope for the best
local mode = "http-chunked"
if headers["content-length"] then mode = "keep-open" end
return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))
end
function metat.__index:receivestatusline()
local status = self.try(self.c:receive(5))
-- identify HTTP/0.9 responses, which do not contain a status line
-- this is just a heuristic, but is what the RFC recommends
if status ~= "HTTP/" then return nil, status end
-- otherwise proceed reading a status line
status = self.try(self.c:receive("*l", status))
local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
return self.try(base.tonumber(code), status)
end
function metat.__index:receiveheaders()
return self.try(receiveheaders(self.c))
end
function metat.__index:receivebody(headers, sink, step)
sink = sink or ltn12.sink.null()
step = step or ltn12.pump.step
local length = base.tonumber(headers["content-length"])
local t = headers["transfer-encoding"] -- shortcut
local mode = "default" -- connection close
if t and t ~= "identity" then mode = "http-chunked"
elseif base.tonumber(headers["content-length"]) then mode = "by-length" end
return self.try(ltn12.pump.all(socket.source(mode, self.c, length),
sink, step))
end
function metat.__index:receive09body(status, sink, step)
local source = ltn12.source.rewind(socket.source("until-closed", self.c))
source(status)
return self.try(ltn12.pump.all(source, sink, step))
end
function metat.__index:close()
return self.c:close()
end
-----------------------------------------------------------------------------
-- High level HTTP API
-----------------------------------------------------------------------------
local function adjusturi(reqt)
local u = reqt
-- if there is a proxy, we need the full url. otherwise, just a part.
if not reqt.proxy and not PROXY then
u = {
path = socket.try(reqt.path, "invalid path 'nil'"),
params = reqt.params,
query = reqt.query,
fragment = reqt.fragment
}
end
return url.build(u)
end
local function adjustproxy(reqt)
local proxy = reqt.proxy or PROXY
if proxy then
proxy = url.parse(proxy)
return proxy.host, proxy.port or 3128
else
return reqt.host, reqt.port
end
end
local function adjustheaders(reqt)
-- default headers
local lower = {
["user-agent"] = USERAGENT,
["host"] = reqt.host,
["connection"] = "close, TE",
["te"] = "trailers"
}
-- if we have authentication information, pass it along
if reqt.user and reqt.password then
lower["authorization"] =
"Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password))
end
-- override with user headers
for i,v in base.pairs(reqt.headers or lower) do
lower[string.lower(i)] = v
end
return lower
end
-- default url parts
local default = {
host = "",
port = PORT,
path ="/",
scheme = "http"
}
local function adjustrequest(reqt)
-- parse url if provided
local nreqt = reqt.url and url.parse(reqt.url, default) or {}
-- explicit components override url
for i,v in base.pairs(reqt) do nreqt[i] = v end
if nreqt.port == "" then nreqt.port = 80 end
socket.try(nreqt.host and nreqt.host ~= "",
"invalid host '" .. base.tostring(nreqt.host) .. "'")
-- compute uri if user hasn't overriden
nreqt.uri = reqt.uri or adjusturi(nreqt)
-- ajust host and port if there is a proxy
nreqt.host, nreqt.port = adjustproxy(nreqt)
-- adjust headers in request
nreqt.headers = adjustheaders(nreqt)
return nreqt
end
local function shouldredirect(reqt, code, headers)
return headers.location and
string.gsub(headers.location, "%s", "") ~= "" and
(reqt.redirect ~= false) and
(code == 301 or code == 302 or code == 303 or code == 307) and
(not reqt.method or reqt.method == "GET" or reqt.method == "HEAD")
and (not reqt.nredirects or reqt.nredirects < 5)
end
local function shouldreceivebody(reqt, code)
if reqt.method == "HEAD" then return nil end
if code == 204 or code == 304 then return nil end
if code >= 100 and code < 200 then return nil end
return 1
end
-- forward declarations
local trequest, tredirect
function tredirect(reqt, location)
local result, code, headers, status = trequest {
-- the RFC says the redirect URL has to be absolute, but some
-- servers do not respect that
url = url.absolute(reqt.url, location),
source = reqt.source,
sink = reqt.sink,
headers = reqt.headers,
proxy = reqt.proxy,
nredirects = (reqt.nredirects or 0) + 1,
create = reqt.create
}
-- pass location header back as a hint we redirected
headers = headers or {}
headers.location = headers.location or location
return result, code, headers, status
end
function trequest(reqt)
-- we loop until we get what we want, or
-- until we are sure there is no way to get it
local nreqt = adjustrequest(reqt)
local h = open(nreqt.host, nreqt.port, nreqt.create)
-- send request line and headers
h:sendrequestline(nreqt.method, nreqt.uri)
h:sendheaders(nreqt.headers)
-- if there is a body, send it
if nreqt.source then
h:sendbody(nreqt.headers, nreqt.source, nreqt.step)
end
local code, status = h:receivestatusline()
-- if it is an HTTP/0.9 server, simply get the body and we are done
if not code then
h:receive09body(status, nreqt.sink, nreqt.step)
return 1, 200
end
local headers
-- ignore any 100-continue messages
while code == 100 do
headers = h:receiveheaders()
code, status = h:receivestatusline()
end
headers = h:receiveheaders()
-- at this point we should have a honest reply from the server
-- we can't redirect if we already used the source, so we report the error
if shouldredirect(nreqt, code, headers) and not nreqt.source then
h:close()
return tredirect(reqt, headers.location)
end
-- here we are finally done
if shouldreceivebody(nreqt, code) then
h:receivebody(headers, nreqt.sink, nreqt.step)
end
h:close()
return 1, code, headers, status
end
local function srequest(u, b)
local t = {}
local reqt = {
url = u,
sink = ltn12.sink.table(t)
}
if b then
reqt.source = ltn12.source.string(b)
reqt.headers = {
["content-length"] = string.len(b),
["content-type"] = "application/x-www-form-urlencoded"
}
reqt.method = "POST"
end
local code, headers, status = socket.skip(1, trequest(reqt))
return table.concat(t), code, headers, status
end
request = socket.protect(function(reqt, body)
if base.type(reqt) == "string" then return srequest(reqt, body)
else return trequest(reqt) end
end)

368
src/inet.c Normal file
View File

@ -0,0 +1,368 @@
/*=========================================================================*\
* Internet domain functions
* LuaSocket toolkit
*
* RCS ID: $Id: inet.c,v 1.28 2005/10/07 04:40:59 diego Exp $
\*=========================================================================*/
#include <stdio.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "inet.h"
/*=========================================================================*\
* Internal function prototypes.
\*=========================================================================*/
static int inet_global_toip(lua_State *L);
static int inet_global_getaddrinfo(lua_State *L);
static int inet_global_tohostname(lua_State *L);
static void inet_pushresolved(lua_State *L, struct hostent *hp);
static int inet_global_gethostname(lua_State *L);
/* DNS functions */
static luaL_Reg func[] = {
{ "toip", inet_global_toip},
{ "getaddrinfo", inet_global_getaddrinfo},
{ "tohostname", inet_global_tohostname},
{ "gethostname", inet_global_gethostname},
{ NULL, NULL}
};
/*=========================================================================*\
* Exported functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int inet_open(lua_State *L)
{
lua_pushstring(L, "dns");
lua_newtable(L);
luaL_openlib(L, NULL, func, 0);
lua_settable(L, -3);
return 0;
}
/*=========================================================================*\
* Global Lua functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Returns all information provided by the resolver given a host name
* or ip address
\*-------------------------------------------------------------------------*/
static int inet_gethost(const char *address, struct hostent **hp) {
struct in_addr addr;
if (inet_aton(address, &addr))
return socket_gethostbyaddr((char *) &addr, sizeof(addr), hp);
else
return socket_gethostbyname(address, hp);
}
/*-------------------------------------------------------------------------*\
* Returns all information provided by the resolver given a host name
* or ip address
\*-------------------------------------------------------------------------*/
static int inet_global_tohostname(lua_State *L) {
const char *address = luaL_checkstring(L, 1);
struct hostent *hp = NULL;
int err = inet_gethost(address, &hp);
if (err != IO_DONE) {
lua_pushnil(L);
lua_pushstring(L, socket_hoststrerror(err));
return 2;
}
lua_pushstring(L, hp->h_name);
inet_pushresolved(L, hp);
return 2;
}
/*-------------------------------------------------------------------------*\
* Returns all information provided by the resolver given a host name
* or ip address
\*-------------------------------------------------------------------------*/
static int inet_global_toip(lua_State *L)
{
const char *address = luaL_checkstring(L, 1);
struct hostent *hp = NULL;
int err = inet_gethost(address, &hp);
if (err != IO_DONE) {
lua_pushnil(L);
lua_pushstring(L, socket_hoststrerror(err));
return 2;
}
lua_pushstring(L, inet_ntoa(*((struct in_addr *) hp->h_addr)));
inet_pushresolved(L, hp);
return 2;
}
static int inet_global_getaddrinfo(lua_State *L)
{
const char *hostname = luaL_checkstring(L, 1);
struct addrinfo *iterator = NULL, *resolved = NULL;
struct addrinfo hints;
int i = 1, ret = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = PF_UNSPEC;
ret = getaddrinfo(hostname, NULL, &hints, &resolved);
if (ret != 0) {
lua_pushnil(L);
lua_pushstring(L, socket_gaistrerror(ret));
return 2;
}
lua_newtable(L);
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
getnameinfo(iterator->ai_addr, iterator->ai_addrlen, hbuf, sizeof(hbuf),
sbuf, 0, NI_NUMERICHOST);
lua_pushnumber(L, i);
lua_newtable(L);
switch (iterator->ai_family) {
case AF_INET:
lua_pushliteral(L, "family");
lua_pushliteral(L, "inet");
lua_settable(L, -3);
break;
case AF_INET6:
lua_pushliteral(L, "family");
lua_pushliteral(L, "inet6");
lua_settable(L, -3);
break;;
}
lua_pushliteral(L, "addr");
lua_pushstring(L, hbuf);
lua_settable(L, -3);
lua_settable(L, -3);
i++;
}
freeaddrinfo(resolved);
return 1;
}
/*-------------------------------------------------------------------------*\
* Gets the host name
\*-------------------------------------------------------------------------*/
static int inet_global_gethostname(lua_State *L)
{
char name[257];
name[256] = '\0';
if (gethostname(name, 256) < 0) {
lua_pushnil(L);
lua_pushstring(L, "gethostname failed");
return 2;
} else {
lua_pushstring(L, name);
return 1;
}
}
/*=========================================================================*\
* Lua methods
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Retrieves socket peer name
\*-------------------------------------------------------------------------*/
int inet_meth_getpeername(lua_State *L, p_socket ps)
{
union {
struct sockaddr_storage sas;
struct sockaddr sa;
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
} peer;
socklen_t peer_len = sizeof(peer);
if (getpeername(*ps, &peer.sa, &peer_len) < 0) {
lua_pushnil(L);
lua_pushfstring(L, "getpeername failed (%d): %s", errno,
strerror(errno));
} else {
char ipaddr[INET6_ADDRSTRLEN] = "";
unsigned short port = 0;
switch (peer.sa.sa_family) {
case AF_INET:
inet_ntop(AF_INET, &peer.sa4.sin_addr, ipaddr, sizeof(ipaddr));
port = ntohs(peer.sa4.sin_port);
break;
case AF_INET6:
inet_ntop(AF_INET6, &peer.sa6.sin6_addr, ipaddr, sizeof(ipaddr));
port = ntohs(peer.sa6.sin6_port);
break;
default:
lua_pushnil(L);
lua_pushfstring(L, "Unknown address family %d", peer.sa.sa_family);
return 2;
break;
}
lua_pushstring(L, ipaddr);
lua_pushnumber(L, port);
}
return 2;
}
/*-------------------------------------------------------------------------*\
* Retrieves socket local name
\*-------------------------------------------------------------------------*/
int inet_meth_getsockname(lua_State *L, p_socket ps)
{
struct sockaddr_in local;
socklen_t local_len = sizeof(local);
if (getsockname(*ps, (SA *) &local, &local_len) < 0) {
lua_pushnil(L);
lua_pushstring(L, "getsockname failed");
} else {
lua_pushstring(L, inet_ntoa(local.sin_addr));
lua_pushnumber(L, ntohs(local.sin_port));
}
return 2;
}
/*=========================================================================*\
* Internal functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Passes all resolver information to Lua as a table
\*-------------------------------------------------------------------------*/
static void inet_pushresolved(lua_State *L, struct hostent *hp)
{
char **alias;
struct in_addr **addr;
int i, resolved;
lua_newtable(L); resolved = lua_gettop(L);
lua_pushstring(L, "name");
lua_pushstring(L, hp->h_name);
lua_settable(L, resolved);
lua_pushstring(L, "ip");
lua_pushstring(L, "alias");
i = 1;
alias = hp->h_aliases;
lua_newtable(L);
if (alias) {
while (*alias) {
lua_pushnumber(L, i);
lua_pushstring(L, *alias);
lua_settable(L, -3);
i++; alias++;
}
}
lua_settable(L, resolved);
i = 1;
lua_newtable(L);
addr = (struct in_addr **) hp->h_addr_list;
if (addr) {
while (*addr) {
lua_pushnumber(L, i);
lua_pushstring(L, inet_ntoa(**addr));
lua_settable(L, -3);
i++; addr++;
}
}
lua_settable(L, resolved);
}
/*-------------------------------------------------------------------------*\
* Tries to create a new inet socket
\*-------------------------------------------------------------------------*/
const char *inet_trycreate(p_socket ps, int domain, int type) {
return socket_strerror(socket_create(ps, domain, type, 0));
}
/*-------------------------------------------------------------------------*\
* Tries to connect to remote address (address, port)
\*-------------------------------------------------------------------------*/
const char *inet_tryconnect(p_socket ps, const char *address,
const char *serv, p_timeout tm, struct addrinfo *connecthints)
{
struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL;
/* try resolving */
err = socket_gaistrerror(getaddrinfo(address, serv,
connecthints, &resolved));
if (err != NULL) {
if (resolved) freeaddrinfo(resolved);
return err;
}
/* iterate over all returned addresses trying to connect */
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
timeout_markstart(tm);
/* try connecting to remote address */
err = socket_strerror(socket_connect(ps,
(SA *) iterator->ai_addr,
iterator->ai_addrlen, tm));
/* if success, break out of loop */
if (err == NULL) break;
}
freeaddrinfo(resolved);
/* here, if err is set, we failed */
return err;
}
/*-------------------------------------------------------------------------*\
* Tries to bind socket to (address, port)
\*-------------------------------------------------------------------------*/
const char *inet_trybind(p_socket ps, const char *address, const char *serv,
struct addrinfo *bindhints)
{
struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL;
/* translate luasocket special values to C */
if (strcmp(address, "*") == 0) address = NULL;
if (!serv) serv = "0";
/* try resolving */
err = socket_gaistrerror(getaddrinfo(address, serv,
bindhints, &resolved));
if (err) {
if (resolved) freeaddrinfo(resolved);
return err;
}
/* iterate over resolved addresses until one is good */
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
/* try binding to local address */
err = socket_strerror(socket_bind(ps,
(SA *) iterator->ai_addr,
iterator->ai_addrlen));
/* if faiiled, we try the next one */
if (err != NULL) socket_destroy(ps);
/* if success, we abort loop */
else break;
}
/* cleanup and return error */
freeaddrinfo(resolved);
return err;
}
/*-------------------------------------------------------------------------*\
* Some systems do not provide this so that we provide our own. It's not
* marvelously fast, but it works just fine.
\*-------------------------------------------------------------------------*/
#ifdef INET_ATON
int inet_aton(const char *cp, struct in_addr *inp)
{
unsigned int a = 0, b = 0, c = 0, d = 0;
int n = 0, r;
unsigned long int addr = 0;
r = sscanf(cp, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n);
if (r == 0 || n == 0) return 0;
cp += n;
if (*cp) return 0;
if (a > 255 || b > 255 || c > 255 || d > 255) return 0;
if (inp) {
addr += a; addr <<= 8;
addr += b; addr <<= 8;
addr += c; addr <<= 8;
addr += d;
inp->s_addr = htonl(addr);
}
return 1;
}
#endif

40
src/inet.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef INET_H
#define INET_H
/*=========================================================================*\
* Internet domain functions
* LuaSocket toolkit
*
* This module implements the creation and connection of internet domain
* sockets, on top of the socket.h interface, and the interface of with the
* resolver.
*
* The function inet_aton is provided for the platforms where it is not
* available. The module also implements the interface of the internet
* getpeername and getsockname functions as seen by Lua programs.
*
* The Lua functions toip and tohostname are also implemented here.
\*=========================================================================*/
#include "lua.h"
#include "socket.h"
#include "timeout.h"
#ifdef _WIN32
#define INET_ATON
#endif
int inet_open(lua_State *L);
const char *inet_trycreate(p_socket ps, int domain, int type);
const char *inet_tryconnect(p_socket ps, const char *address,
const char *serv, p_timeout tm, struct addrinfo *connecthints);
const char *inet_trybind(p_socket ps, const char *address, const char *serv,
struct addrinfo *bindhints);
int inet_meth_getpeername(lua_State *L, p_socket ps);
int inet_meth_getsockname(lua_State *L, p_socket ps);
#ifdef INET_ATON
int inet_aton(const char *cp, struct in_addr *inp);
#endif
#endif /* INET_H */

BIN
src/inet.o Normal file

Binary file not shown.

32
src/io.c Normal file
View File

@ -0,0 +1,32 @@
/*=========================================================================*\
* Input/Output abstraction
* LuaSocket toolkit
*
* RCS ID: $Id: io.c,v 1.6 2005/09/29 06:11:41 diego Exp $
\*=========================================================================*/
#include "io.h"
/*=========================================================================*\
* Exported functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Initializes C structure
\*-------------------------------------------------------------------------*/
void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx) {
io->send = send;
io->recv = recv;
io->error = error;
io->ctx = ctx;
}
/*-------------------------------------------------------------------------*\
* I/O error strings
\*-------------------------------------------------------------------------*/
const char *io_strerror(int err) {
switch (err) {
case IO_DONE: return NULL;
case IO_CLOSED: return "closed";
case IO_TIMEOUT: return "timeout";
default: return "unknown error";
}
}

65
src/io.h Normal file
View File

@ -0,0 +1,65 @@
#ifndef IO_H
#define IO_H
/*=========================================================================*\
* Input/Output abstraction
* LuaSocket toolkit
*
* This module defines the interface that LuaSocket expects from the
* transport layer for streamed input/output. The idea is that if any
* transport implements this interface, then the buffer.c functions
* automatically work on it.
*
* The module socket.h implements this interface, and thus the module tcp.h
* is very simple.
\*=========================================================================*/
#include <stdio.h>
#include "lua.h"
#include "timeout.h"
/* IO error codes */
enum {
IO_DONE = 0, /* operation completed successfully */
IO_TIMEOUT = -1, /* operation timed out */
IO_CLOSED = -2, /* the connection has been closed */
IO_UNKNOWN = -3
};
/* interface to error message function */
typedef const char *(*p_error) (
void *ctx, /* context needed by send */
int err /* error code */
);
/* interface to send function */
typedef int (*p_send) (
void *ctx, /* context needed by send */
const char *data, /* pointer to buffer with data to send */
size_t count, /* number of bytes to send from buffer */
size_t *sent, /* number of bytes sent uppon return */
p_timeout tm /* timeout control */
);
/* interface to recv function */
typedef int (*p_recv) (
void *ctx, /* context needed by recv */
char *data, /* pointer to buffer where data will be writen */
size_t count, /* number of bytes to receive into buffer */
size_t *got, /* number of bytes received uppon return */
p_timeout tm /* timeout control */
);
/* IO driver definition */
typedef struct t_io_ {
void *ctx; /* context needed by send/recv */
p_send send; /* send function pointer */
p_recv recv; /* receive function pointer */
p_error error; /* strerror function */
} t_io;
typedef t_io *p_io;
void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx);
const char *io_strerror(int err);
#endif /* IO_H */

BIN
src/io.o Normal file

Binary file not shown.

292
src/ltn12.lua Normal file
View File

@ -0,0 +1,292 @@
-----------------------------------------------------------------------------
-- LTN12 - Filters, sources, sinks and pumps.
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: ltn12.lua,v 1.31 2006/04/03 04:45:42 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local table = require("table")
local base = _G
module("ltn12")
filter = {}
source = {}
sink = {}
pump = {}
-- 2048 seems to be better in windows...
BLOCKSIZE = 2048
_VERSION = "LTN12 1.0.1"
-----------------------------------------------------------------------------
-- Filter stuff
-----------------------------------------------------------------------------
-- returns a high level filter that cycles a low-level filter
function filter.cycle(low, ctx, extra)
base.assert(low)
return function(chunk)
local ret
ret, ctx = low(ctx, chunk, extra)
return ret
end
end
-- chains a bunch of filters together
-- (thanks to Wim Couwenberg)
function filter.chain(...)
local n = table.getn(arg)
local top, index = 1, 1
local retry = ""
return function(chunk)
retry = chunk and retry
while true do
if index == top then
chunk = arg[index](chunk)
if chunk == "" or top == n then return chunk
elseif chunk then index = index + 1
else
top = top+1
index = top
end
else
chunk = arg[index](chunk or "")
if chunk == "" then
index = index - 1
chunk = retry
elseif chunk then
if index == n then return chunk
else index = index + 1 end
else base.error("filter returned inappropriate nil") end
end
end
end
end
-----------------------------------------------------------------------------
-- Source stuff
-----------------------------------------------------------------------------
-- create an empty source
local function empty()
return nil
end
function source.empty()
return empty
end
-- returns a source that just outputs an error
function source.error(err)
return function()
return nil, err
end
end
-- creates a file source
function source.file(handle, io_err)
if handle then
return function()
local chunk = handle:read(BLOCKSIZE)
if not chunk then handle:close() end
return chunk
end
else return source.error(io_err or "unable to open file") end
end
-- turns a fancy source into a simple source
function source.simplify(src)
base.assert(src)
return function()
local chunk, err_or_new = src()
src = err_or_new or src
if not chunk then return nil, err_or_new
else return chunk end
end
end
-- creates string source
function source.string(s)
if s then
local i = 1
return function()
local chunk = string.sub(s, i, i+BLOCKSIZE-1)
i = i + BLOCKSIZE
if chunk ~= "" then return chunk
else return nil end
end
else return source.empty() end
end
-- creates rewindable source
function source.rewind(src)
base.assert(src)
local t = {}
return function(chunk)
if not chunk then
chunk = table.remove(t)
if not chunk then return src()
else return chunk end
else
table.insert(t, chunk)
end
end
end
function source.chain(src, f)
base.assert(src and f)
local last_in, last_out = "", ""
local state = "feeding"
local err
return function()
if not last_out then
base.error('source is empty!', 2)
end
while true do
if state == "feeding" then
last_in, err = src()
if err then return nil, err end
last_out = f(last_in)
if not last_out then
if last_in then
base.error('filter returned inappropriate nil')
else
return nil
end
elseif last_out ~= "" then
state = "eating"
if last_in then last_in = "" end
return last_out
end
else
last_out = f(last_in)
if last_out == "" then
if last_in == "" then
state = "feeding"
else
base.error('filter returned ""')
end
elseif not last_out then
if last_in then
base.error('filter returned inappropriate nil')
else
return nil
end
else
return last_out
end
end
end
end
end
-- creates a source that produces contents of several sources, one after the
-- other, as if they were concatenated
-- (thanks to Wim Couwenberg)
function source.cat(...)
local src = table.remove(arg, 1)
return function()
while src do
local chunk, err = src()
if chunk then return chunk end
if err then return nil, err end
src = table.remove(arg, 1)
end
end
end
-----------------------------------------------------------------------------
-- Sink stuff
-----------------------------------------------------------------------------
-- creates a sink that stores into a table
function sink.table(t)
t = t or {}
local f = function(chunk, err)
if chunk then table.insert(t, chunk) end
return 1
end
return f, t
end
-- turns a fancy sink into a simple sink
function sink.simplify(snk)
base.assert(snk)
return function(chunk, err)
local ret, err_or_new = snk(chunk, err)
if not ret then return nil, err_or_new end
snk = err_or_new or snk
return 1
end
end
-- creates a file sink
function sink.file(handle, io_err)
if handle then
return function(chunk, err)
if not chunk then
handle:close()
return 1
else return handle:write(chunk) end
end
else return sink.error(io_err or "unable to open file") end
end
-- creates a sink that discards data
local function null()
return 1
end
function sink.null()
return null
end
-- creates a sink that just returns an error
function sink.error(err)
return function()
return nil, err
end
end
-- chains a sink with a filter
function sink.chain(f, snk)
base.assert(f and snk)
return function(chunk, err)
if chunk ~= "" then
local filtered = f(chunk)
local done = chunk and ""
while true do
local ret, snkerr = snk(filtered, err)
if not ret then return nil, snkerr end
if filtered == done then return 1 end
filtered = f(done)
end
else return 1 end
end
end
-----------------------------------------------------------------------------
-- Pump stuff
-----------------------------------------------------------------------------
-- pumps one chunk from the source to the sink
function pump.step(src, snk)
local chunk, src_err = src()
local ret, snk_err = snk(chunk, src_err)
if chunk and ret then return 1
else return nil, src_err or snk_err end
end
-- pumps all data from a source to a sink, using a step function
function pump.all(src, snk, step)
base.assert(src and snk)
step = step or pump.step
while true do
local ret, err = step(src, snk)
if not ret then
if err then return nil, err
else return 1 end
end
end
end

10
src/lua_typeerror.c Normal file
View File

@ -0,0 +1,10 @@
#include "lua_typeerror.h"
#include "lua.h"
#include "lauxlib.h"
int luaL_typeerror (lua_State *L, int narg, const char *tname)
{
const char *msg = lua_pushfstring(L, "%s expected, got %s",tname, luaL_typename(L, narg));
return luaL_argerror(L, narg, msg);
}

7
src/lua_typeerror.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef LUA_TYPEERROR_H_
#define LUA_TYPEERROR_H_
struct lua_State;
int luaL_typeerror (struct lua_State *L, int narg, const char *tname);
#endif

116
src/luasocket.c Normal file
View File

@ -0,0 +1,116 @@
/*=========================================================================*\
* LuaSocket toolkit
* Networking support for the Lua language
* Diego Nehab
* 26/11/1999
*
* This library is part of an effort to progressively increase the network
* connectivity of the Lua language. The Lua interface to networking
* functions follows the Sockets API closely, trying to simplify all tasks
* involved in setting up both client and server connections. The provided
* IO routines, however, follow the Lua style, being very similar to the
* standard Lua read and write functions.
\*=========================================================================*/
/*=========================================================================*\
* Standard include files
\*=========================================================================*/
#include "lua.h"
#include "lauxlib.h"
#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM < 501)
#include "compat-5.1.h"
#endif
/*=========================================================================*\
* LuaSocket includes
\*=========================================================================*/
#include "luasocket.h"
#include "auxiliar.h"
#include "except.h"
#include "timeout.h"
#include "buffer.h"
#include "inet.h"
#include "tcp.h"
#include "udp.h"
#include "select.h"
/*-------------------------------------------------------------------------*\
* Internal function prototypes
\*-------------------------------------------------------------------------*/
static int global_skip(lua_State *L);
static int global_unload(lua_State *L);
static int base_open(lua_State *L);
/*-------------------------------------------------------------------------*\
* Modules and functions
\*-------------------------------------------------------------------------*/
static const luaL_Reg mod[] = {
{"auxiliar", auxiliar_open},
{"except", except_open},
{"timeout", timeout_open},
{"buffer", buffer_open},
{"inet", inet_open},
{"tcp", tcp_open},
{"udp", udp_open},
{"select", select_open},
{NULL, NULL}
};
static luaL_Reg func[] = {
{"skip", global_skip},
{"__unload", global_unload},
{NULL, NULL}
};
/*-------------------------------------------------------------------------*\
* Skip a few arguments
\*-------------------------------------------------------------------------*/
static int global_skip(lua_State *L) {
int amount = luaL_checkint(L, 1);
int ret = lua_gettop(L) - amount - 1;
return ret >= 0 ? ret : 0;
}
/*-------------------------------------------------------------------------*\
* Unloads the library
\*-------------------------------------------------------------------------*/
static int global_unload(lua_State *L) {
(void) L;
socket_close();
return 0;
}
/*-------------------------------------------------------------------------*\
* Setup basic stuff.
\*-------------------------------------------------------------------------*/
static int base_open(lua_State *L) {
if (socket_open()) {
/* export functions (and leave namespace table on top of stack) */
luaL_openlib(L, "socket", func, 0);
#ifdef LUASOCKET_DEBUG
lua_pushstring(L, "_DEBUG");
lua_pushboolean(L, 1);
lua_rawset(L, -3);
#endif
/* make version string available to scripts */
lua_pushstring(L, "_VERSION");
lua_pushstring(L, LUASOCKET_VERSION);
lua_rawset(L, -3);
return 1;
} else {
lua_pushstring(L, "unable to initialize library");
lua_error(L);
return 0;
}
}
/*-------------------------------------------------------------------------*\
* Initializes all library modules.
\*-------------------------------------------------------------------------*/
LUASOCKET_API int luaopen_socket_core(lua_State *L) {
int i;
base_open(L);
for (i = 0; mod[i].name; i++) mod[i].func(L);
return 1;
}

34
src/luasocket.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef LUASOCKET_H
#define LUASOCKET_H
/*=========================================================================*\
* LuaSocket toolkit
* Networking support for the Lua language
* Diego Nehab
* 9/11/1999
\*=========================================================================*/
#include "lua.h"
/*-------------------------------------------------------------------------*\
* Current socket library version
\*-------------------------------------------------------------------------*/
#define LUASOCKET_VERSION "LuaSocket 2.1.1"
#define LUASOCKET_COPYRIGHT "Copyright (C) 1999-2011 Diego Nehab"
#define LUASOCKET_AUTHORS "Diego Nehab"
/*-------------------------------------------------------------------------*\
* This macro prefixes all exported API functions
\*-------------------------------------------------------------------------*/
#ifndef LUASOCKET_API
#define LUASOCKET_API extern
#endif
#if LUA_VERSION_NUM > 501 & !( defined LUA_COMPAT_MODULE)
# error Lua 5.2 requires LUA_COMPAT_MODULE defined for luaL_openlib
#endif
/*-------------------------------------------------------------------------*\
* Initializes the library.
\*-------------------------------------------------------------------------*/
LUASOCKET_API int luaopen_socket_core(lua_State *L);
#endif /* LUASOCKET_H */

BIN
src/luasocket.o Normal file

Binary file not shown.

239
src/makefile Normal file
View File

@ -0,0 +1,239 @@
PLAT?=macosx
INSTALL_DATA=cp
INSTALL_EXEC=cp
#INSTALL_TOP=/opt/local
INSTALL_TOP=./
#LUAINC_macosx=/opt/local/include
LUAINC_macosx=../../../../projects/lua_env/luaenv/lua_versions/lua-5.2.0-beta/src
#LUAINC_macosx=../../../../projects/lua_env/luaenv/lua_versions/lua-5.1.4/src
LUAINC_linux=/usr/include/lua5.1
LUAINC_win32="../../lua-5.1.3/src"
LUALIB_win32="../../lua-5.1.3"
#------
# Install directories
#
#INSTALL_TOP_SHARE=$(INSTALL_TOP)/share/lua/5.1
#INSTALL_TOP_LIB=$(INSTALL_TOP)/lib/lua/5.1
INSTALL_TOP_SHARE=$(INSTALL_TOP)/share/lua/5.2
INSTALL_TOP_LIB=$(INSTALL_TOP)/lib/lua/5.2
INSTALL_SOCKET_SHARE=$(INSTALL_TOP_SHARE)/socket
INSTALL_SOCKET_LIB=$(INSTALL_TOP_LIB)/socket
#INSTALL_MIME_SHARE=$(INSTALL_TOP_SHARE)/mime
INSTALL_MIME_SHARE=$(INSTALL_TOP_SHARE)/foo/mime
INSTALL_MIME_LIB=$(INSTALL_TOP_LIB)/mime
#------
# Supported platforms
#
PLATS= macosx linux win32
#------
# Compiler and linker settings
# for Mac OS X
SO_macosx=so
O_macosx=o
CC_macosx=gcc
DEF_macosx= -DLUASOCKET_DEBUG -DUNIX_HAS_SUN_LEN -DLUA_COMPAT_MODULE \
-DLUASOCKET_API='__attribute__((visibility("default")))' \
-DMIME_API='__attribute__((visibility("default")))'
CFLAGS_macosx= -I$(LUAINC) $(DEF) -pedantic -Wall -O2 -fno-common \
-fvisibility=hidden
LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o
LD_macosx= export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc
SOCKET_macosx=usocket.o
#------
# Compiler and linker settings
# for Linux
SO_linux=so
O_linux=o
CC_linux=gcc
DEF_linux=-DLUASOCKET_DEBUG \
-DLUASOCKET_API='__attribute__((visibility("default")))' \
-DMIME_API='__attribute__((visibility("default")))'
CFLAGS_linux= -I$(LUAINC) $(DEF) -pedantic -Wall -Wshadow -Wextra -Wimplicit -O2 -ggdb3 -fpic \
-fvisibility=hidden
LDFLAGS_linux=-O -shared -fpic -o
LD_linux=gcc
SOCKET_linux=usocket.o
#------
# Compiler and linker settings
# for Win32
SO_win32=dll
O_win32=obj
CC_win32=cl
DEF_win32= /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" \
/D "LUASOCKET_API=__declspec(dllexport)" /D "LUASOCKET_DEBUG" \
/D "_CRT_SECURE_NO_WARNINGS" /D "_WINDLL"
CFLAGS_win32=/I$(LUAINC) $(DEF) /O2 /Ot /MD /W3 /nologo
LDFLAGS_win32= /nologo /link /NOLOGO /DLL /INCREMENTAL:NO \
/LIBPATH:$(LUALIB) \
/MANIFEST \
/MANIFESTFILE:"intermediate.manifest" \
/MANIFESTUAC:"level='asInvoker' uiAccess='false'" \
/SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF /DYNAMICBASE:NO \
/MACHINE:X86 ws2_32.lib lua5.1.lib /OUT:
LD_win32=cl
SOCKET_win32=wsocket.obj
.SUFFIXES: .obj
.c.obj:
$(CC) $(CFLAGS) /Fo"$@" /c $<
#------
# Output file names
#
SO=$(SO_$(PLAT))
O=$(O_$(PLAT))
SOCKET_V=2.1.1
MIME_V=1.0.3
SOCKET_SO=socket.$(SO).$(SOCKET_V)
MIME_SO=mime.$(SO).$(MIME_V)
UNIX_SO=unix.$(SO)
SOCKET=$(SOCKET_$(PLAT))
#------
# Settings selected for platform
#
CC=$(CC_$(PLAT))
DEF=$(DEF_$(PLAT))
CFLAGS=$(CFLAGS_$(PLAT))
LDFLAGS=$(LDFLAGS_$(PLAT))
LD=$(LD_$(PLAT))
LUAINC= $(LUAINC_$(PLAT))
LUALIB= $(LUALIB_$(PLAT))
#------
# Modules belonging to socket-core
#
SOCKET_OBJS= \
luasocket.$(O) \
timeout.$(O) \
buffer.$(O) \
io.$(O) \
auxiliar.$(O) \
options.$(O) \
inet.$(O) \
$(SOCKET) \
except.$(O) \
select.$(O) \
tcp.$(O) \
udp.$(O) \
lua_typeerror.$(O)
#------
# Modules belonging mime-core
#
MIME_OBJS= \
mime.$(O)
#------
# Modules belonging unix (local domain sockets)
#
UNIX_OBJS=\
buffer.$(O) \
auxiliar.$(O) \
options.$(O) \
timeout.$(O) \
io.$(O) \
usocket.$(O) \
unix.$(O) \
lua_typeerror.$(O)
#------
# Files to install
#
TO_SOCKET_SHARE= \
http.lua \
url.lua \
tp.lua \
ftp.lua \
headers.lua \
smtp.lua
TO_TOP_SHARE= \
ltn12.lua \
socket.lua \
mime.lua
#------
# Targets
#
default: $(PLAT)
macosx:
$(MAKE) all PLAT=macosx
win32:
$(MAKE) all PLAT=win32
linux:
$(MAKE) all PLAT=linux
none:
@echo "Please run"
@echo " make PLATFORM"
@echo "where PLATFORM is one of these:"
@echo " $(PLATS)"
all: $(SOCKET_SO) $(MIME_SO)
$(SOCKET_SO): $(SOCKET_OBJS)
$(LD) $(SOCKET_OBJS) $(LDFLAGS)$@
$(MIME_SO): $(MIME_OBJS)
$(LD) $(MIME_OBJS) $(LDFLAGS)$@
$(UNIX_SO): $(UNIX_OBJS)
$(LD) $(UNIX_OBJS) $(LDFLAGS)$@
install:
mkdir -p $(INSTALL_TOP_SHARE)
$(INSTALL_DATA) $(TO_TOP_SHARE) $(INSTALL_TOP_SHARE)
mkdir -p $(INSTALL_SOCKET_SHARE)
$(INSTALL_DATA) $(TO_SOCKET_SHARE) $(INSTALL_SOCKET_SHARE)
mkdir -p $(INSTALL_SOCKET_LIB)
$(INSTALL_EXEC) $(SOCKET_SO) $(INSTALL_SOCKET_LIB)/core.$(SO)
mkdir -p $(INSTALL_MIME_LIB)
$(INSTALL_EXEC) $(MIME_SO) $(INSTALL_MIME_LIB)/core.$(SO)
local:
$(MAKE) install INSTALL_TOP_LIB=.. INSTALL_TOP_SHARE=..
clean:
rm -f $(SOCKET_SO) $(SOCKET_OBJS)
rm -f $(MIME_SO) $(UNIX_SO) $(MIME_OBJS) $(UNIX_OBJS)
.PHONY: all $(PLATS) default clean echo none
#------
# List of dependencies
#
auxiliar.$(O): auxiliar.c auxiliar.h
buffer.$(O): buffer.c buffer.h io.h timeout.h
except.$(O): except.c except.h
inet.$(O): inet.c inet.h socket.h io.h timeout.h usocket.h
io.$(O): io.c io.h timeout.h
luasocket.$(O): luasocket.c luasocket.h auxiliar.h except.h \
timeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \
udp.h select.h
mime.$(O): mime.c mime.h
options.$(O): options.c auxiliar.h options.h socket.h io.h \
timeout.h usocket.h inet.h
select.$(O): select.c socket.h io.h timeout.h usocket.h select.h
tcp.$(O): tcp.c auxiliar.h socket.h io.h timeout.h usocket.h \
inet.h options.h tcp.h buffer.h
timeout.$(O): timeout.c auxiliar.h timeout.h
udp.$(O): udp.c auxiliar.h socket.h io.h timeout.h usocket.h \
inet.h options.h udp.h
unix.$(O): unix.c auxiliar.h socket.h io.h timeout.h usocket.h \
options.h unix.h buffer.h
usocket.$(O): usocket.c socket.h io.h timeout.h usocket.h
wsocket.$(O): wsocket.c socket.h io.h timeout.h usocket.h

717
src/mime.c Normal file
View File

@ -0,0 +1,717 @@
/*=========================================================================*\
* MIME support functions
* LuaSocket toolkit
*
* RCS ID: $Id: mime.c,v 1.29 2009/05/27 09:31:35 diego Exp $
\*=========================================================================*/
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM < 501)
#include "compat-5.1.h"
#endif
#include "mime.h"
/*=========================================================================*\
* Don't want to trust escape character constants
\*=========================================================================*/
typedef unsigned char UC;
static const char CRLF[] = "\r\n";
static const char EQCRLF[] = "=\r\n";
/*=========================================================================*\
* Internal function prototypes.
\*=========================================================================*/
static int mime_global_wrp(lua_State *L);
static int mime_global_b64(lua_State *L);
static int mime_global_unb64(lua_State *L);
static int mime_global_qp(lua_State *L);
static int mime_global_unqp(lua_State *L);
static int mime_global_qpwrp(lua_State *L);
static int mime_global_eol(lua_State *L);
static int mime_global_dot(lua_State *L);
static size_t dot(int c, size_t state, luaL_Buffer *buffer);
static void b64setup(UC *base);
static size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
static size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer);
static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
static void qpsetup(UC *class, UC *unbase);
static void qpquote(UC c, luaL_Buffer *buffer);
static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer);
static size_t qpencode(UC c, UC *input, size_t size,
const char *marker, luaL_Buffer *buffer);
static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer);
/* code support functions */
static luaL_Reg func[] = {
{ "dot", mime_global_dot },
{ "b64", mime_global_b64 },
{ "eol", mime_global_eol },
{ "qp", mime_global_qp },
{ "qpwrp", mime_global_qpwrp },
{ "unb64", mime_global_unb64 },
{ "unqp", mime_global_unqp },
{ "wrp", mime_global_wrp },
{ NULL, NULL }
};
/*-------------------------------------------------------------------------*\
* Quoted-printable globals
\*-------------------------------------------------------------------------*/
static UC qpclass[256];
static UC qpbase[] = "0123456789ABCDEF";
static UC qpunbase[256];
enum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST};
/*-------------------------------------------------------------------------*\
* Base64 globals
\*-------------------------------------------------------------------------*/
static const UC b64base[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static UC b64unbase[256];
/*=========================================================================*\
* Exported functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
MIME_API int luaopen_mime_core(lua_State *L)
{
luaL_openlib(L, "mime", func, 0);
/* make version string available to scripts */
lua_pushstring(L, "_VERSION");
lua_pushstring(L, MIME_VERSION);
lua_rawset(L, -3);
/* initialize lookup tables */
qpsetup(qpclass, qpunbase);
b64setup(b64unbase);
return 1;
}
/*=========================================================================*\
* Global Lua functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Incrementaly breaks a string into lines. The string can have CRLF breaks.
* A, n = wrp(l, B, length)
* A is a copy of B, broken into lines of at most 'length' bytes.
* 'l' is how many bytes are left for the first line of B.
* 'n' is the number of bytes left in the last line of A.
\*-------------------------------------------------------------------------*/
static int mime_global_wrp(lua_State *L)
{
size_t size = 0;
int left = (int) luaL_checknumber(L, 1);
const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &size);
const UC *last = input + size;
int length = (int) luaL_optnumber(L, 3, 76);
luaL_Buffer buffer;
/* end of input black-hole */
if (!input) {
/* if last line has not been terminated, add a line break */
if (left < length) lua_pushstring(L, CRLF);
/* otherwise, we are done */
else lua_pushnil(L);
lua_pushnumber(L, length);
return 2;
}
luaL_buffinit(L, &buffer);
while (input < last) {
switch (*input) {
case '\r':
break;
case '\n':
luaL_addstring(&buffer, CRLF);
left = length;
break;
default:
if (left <= 0) {
left = length;
luaL_addstring(&buffer, CRLF);
}
luaL_addchar(&buffer, *input);
left--;
break;
}
input++;
}
luaL_pushresult(&buffer);
lua_pushnumber(L, left);
return 2;
}
/*-------------------------------------------------------------------------*\
* Fill base64 decode map.
\*-------------------------------------------------------------------------*/
static void b64setup(UC *unbase)
{
int i;
for (i = 0; i <= 255; i++) unbase[i] = (UC) 255;
for (i = 0; i < 64; i++) unbase[b64base[i]] = (UC) i;
unbase['='] = 0;
}
/*-------------------------------------------------------------------------*\
* Acumulates bytes in input buffer until 3 bytes are available.
* Translate the 3 bytes into Base64 form and append to buffer.
* Returns new number of bytes in buffer.
\*-------------------------------------------------------------------------*/
static size_t b64encode(UC c, UC *input, size_t size,
luaL_Buffer *buffer)
{
input[size++] = c;
if (size == 3) {
UC code[4];
unsigned long value = 0;
value += input[0]; value <<= 8;
value += input[1]; value <<= 8;
value += input[2];
code[3] = b64base[value & 0x3f]; value >>= 6;
code[2] = b64base[value & 0x3f]; value >>= 6;
code[1] = b64base[value & 0x3f]; value >>= 6;
code[0] = b64base[value];
luaL_addlstring(buffer, (char *) code, 4);
size = 0;
}
return size;
}
/*-------------------------------------------------------------------------*\
* Encodes the Base64 last 1 or 2 bytes and adds padding '='
* Result, if any, is appended to buffer.
* Returns 0.
\*-------------------------------------------------------------------------*/
static size_t b64pad(const UC *input, size_t size,
luaL_Buffer *buffer)
{
unsigned long value = 0;
UC code[4] = {'=', '=', '=', '='};
switch (size) {
case 1:
value = input[0] << 4;
code[1] = b64base[value & 0x3f]; value >>= 6;
code[0] = b64base[value];
luaL_addlstring(buffer, (char *) code, 4);
break;
case 2:
value = input[0]; value <<= 8;
value |= input[1]; value <<= 2;
code[2] = b64base[value & 0x3f]; value >>= 6;
code[1] = b64base[value & 0x3f]; value >>= 6;
code[0] = b64base[value];
luaL_addlstring(buffer, (char *) code, 4);
break;
default:
break;
}
return 0;
}
/*-------------------------------------------------------------------------*\
* Acumulates bytes in input buffer until 4 bytes are available.
* Translate the 4 bytes from Base64 form and append to buffer.
* Returns new number of bytes in buffer.
\*-------------------------------------------------------------------------*/
static size_t b64decode(UC c, UC *input, size_t size,
luaL_Buffer *buffer)
{
/* ignore invalid characters */
if (b64unbase[c] > 64) return size;
input[size++] = c;
/* decode atom */
if (size == 4) {
UC decoded[3];
int valid, value = 0;
value = b64unbase[input[0]]; value <<= 6;
value |= b64unbase[input[1]]; value <<= 6;
value |= b64unbase[input[2]]; value <<= 6;
value |= b64unbase[input[3]];
decoded[2] = (UC) (value & 0xff); value >>= 8;
decoded[1] = (UC) (value & 0xff); value >>= 8;
decoded[0] = (UC) value;
/* take care of paddding */
valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3;
luaL_addlstring(buffer, (char *) decoded, valid);
return 0;
/* need more data */
} else return size;
}
/*-------------------------------------------------------------------------*\
* Incrementally applies the Base64 transfer content encoding to a string
* A, B = b64(C, D)
* A is the encoded version of the largest prefix of C .. D that is
* divisible by 3. B has the remaining bytes of C .. D, *without* encoding.
* The easiest thing would be to concatenate the two strings and
* encode the result, but we can't afford that or Lua would dupplicate
* every chunk we received.
\*-------------------------------------------------------------------------*/
static int mime_global_b64(lua_State *L)
{
UC atom[3];
size_t isize = 0, asize = 0;
const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize);
const UC *last = input + isize;
luaL_Buffer buffer;
/* end-of-input blackhole */
if (!input) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}
/* process first part of the input */
luaL_buffinit(L, &buffer);
while (input < last)
asize = b64encode(*input++, atom, asize, &buffer);
input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
/* if second part is nil, we are done */
if (!input) {
size_t osize = 0;
asize = b64pad(atom, asize, &buffer);
luaL_pushresult(&buffer);
/* if the output is empty and the input is nil, return nil */
lua_tolstring(L, -1, &osize);
if (osize == 0) lua_pushnil(L);
lua_pushnil(L);
return 2;
}
/* otherwise process the second part */
last = input + isize;
while (input < last)
asize = b64encode(*input++, atom, asize, &buffer);
luaL_pushresult(&buffer);
lua_pushlstring(L, (char *) atom, asize);
return 2;
}
/*-------------------------------------------------------------------------*\
* Incrementally removes the Base64 transfer content encoding from a string
* A, B = b64(C, D)
* A is the encoded version of the largest prefix of C .. D that is
* divisible by 4. B has the remaining bytes of C .. D, *without* encoding.
\*-------------------------------------------------------------------------*/
static int mime_global_unb64(lua_State *L)
{
UC atom[4];
size_t isize = 0, asize = 0;
const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize);
const UC *last = input + isize;
luaL_Buffer buffer;
/* end-of-input blackhole */
if (!input) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}
/* process first part of the input */
luaL_buffinit(L, &buffer);
while (input < last)
asize = b64decode(*input++, atom, asize, &buffer);
input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
/* if second is nil, we are done */
if (!input) {
size_t osize = 0;
luaL_pushresult(&buffer);
/* if the output is empty and the input is nil, return nil */
lua_tolstring(L, -1, &osize);
if (osize == 0) lua_pushnil(L);
lua_pushnil(L);
return 2;
}
/* otherwise, process the rest of the input */
last = input + isize;
while (input < last)
asize = b64decode(*input++, atom, asize, &buffer);
luaL_pushresult(&buffer);
lua_pushlstring(L, (char *) atom, asize);
return 2;
}
/*-------------------------------------------------------------------------*\
* Quoted-printable encoding scheme
* all (except CRLF in text) can be =XX
* CLRL in not text must be =XX=XX
* 33 through 60 inclusive can be plain
* 62 through 126 inclusive can be plain
* 9 and 32 can be plain, unless in the end of a line, where must be =XX
* encoded lines must be no longer than 76 not counting CRLF
* soft line-break are =CRLF
* To encode one byte, we need to see the next two.
* Worst case is when we see a space, and wonder if a CRLF is comming
\*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*\
* Split quoted-printable characters into classes
* Precompute reverse map for encoding
\*-------------------------------------------------------------------------*/
static void qpsetup(UC *cl, UC *unbase)
{
int i;
for (i = 0; i < 256; i++) cl[i] = QP_QUOTED;
for (i = 33; i <= 60; i++) cl[i] = QP_PLAIN;
for (i = 62; i <= 126; i++) cl[i] = QP_PLAIN;
cl['\t'] = QP_IF_LAST;
cl[' '] = QP_IF_LAST;
cl['\r'] = QP_CR;
for (i = 0; i < 256; i++) unbase[i] = 255;
unbase['0'] = 0; unbase['1'] = 1; unbase['2'] = 2;
unbase['3'] = 3; unbase['4'] = 4; unbase['5'] = 5;
unbase['6'] = 6; unbase['7'] = 7; unbase['8'] = 8;
unbase['9'] = 9; unbase['A'] = 10; unbase['a'] = 10;
unbase['B'] = 11; unbase['b'] = 11; unbase['C'] = 12;
unbase['c'] = 12; unbase['D'] = 13; unbase['d'] = 13;
unbase['E'] = 14; unbase['e'] = 14; unbase['F'] = 15;
unbase['f'] = 15;
}
/*-------------------------------------------------------------------------*\
* Output one character in form =XX
\*-------------------------------------------------------------------------*/
static void qpquote(UC c, luaL_Buffer *buffer)
{
luaL_addchar(buffer, '=');
luaL_addchar(buffer, qpbase[c >> 4]);
luaL_addchar(buffer, qpbase[c & 0x0F]);
}
/*-------------------------------------------------------------------------*\
* Accumulate characters until we are sure about how to deal with them.
* Once we are sure, output to the buffer, in the correct form.
\*-------------------------------------------------------------------------*/
static size_t qpencode(UC c, UC *input, size_t size,
const char *marker, luaL_Buffer *buffer)
{
input[size++] = c;
/* deal with all characters we can have */
while (size > 0) {
switch (qpclass[input[0]]) {
/* might be the CR of a CRLF sequence */
case QP_CR:
if (size < 2) return size;
if (input[1] == '\n') {
luaL_addstring(buffer, marker);
return 0;
} else qpquote(input[0], buffer);
break;
/* might be a space and that has to be quoted if last in line */
case QP_IF_LAST:
if (size < 3) return size;
/* if it is the last, quote it and we are done */
if (input[1] == '\r' && input[2] == '\n') {
qpquote(input[0], buffer);
luaL_addstring(buffer, marker);
return 0;
} else luaL_addchar(buffer, input[0]);
break;
/* might have to be quoted always */
case QP_QUOTED:
qpquote(input[0], buffer);
break;
/* might never have to be quoted */
default:
luaL_addchar(buffer, input[0]);
break;
}
input[0] = input[1]; input[1] = input[2];
size--;
}
return 0;
}
/*-------------------------------------------------------------------------*\
* Deal with the final characters
\*-------------------------------------------------------------------------*/
static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer)
{
size_t i;
for (i = 0; i < size; i++) {
if (qpclass[input[i]] == QP_PLAIN) luaL_addchar(buffer, input[i]);
else qpquote(input[i], buffer);
}
if (size > 0) luaL_addstring(buffer, EQCRLF);
return 0;
}
/*-------------------------------------------------------------------------*\
* Incrementally converts a string to quoted-printable
* A, B = qp(C, D, marker)
* Marker is the text to be used to replace CRLF sequences found in A.
* A is the encoded version of the largest prefix of C .. D that
* can be encoded without doubts.
* B has the remaining bytes of C .. D, *without* encoding.
\*-------------------------------------------------------------------------*/
static int mime_global_qp(lua_State *L)
{
size_t asize = 0, isize = 0;
UC atom[3];
const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize);
const UC *last = input + isize;
const char *marker = luaL_optstring(L, 3, CRLF);
luaL_Buffer buffer;
/* end-of-input blackhole */
if (!input) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}
/* process first part of input */
luaL_buffinit(L, &buffer);
while (input < last)
asize = qpencode(*input++, atom, asize, marker, &buffer);
input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
/* if second part is nil, we are done */
if (!input) {
asize = qppad(atom, asize, &buffer);
luaL_pushresult(&buffer);
if (!(*lua_tostring(L, -1))) lua_pushnil(L);
lua_pushnil(L);
return 2;
}
/* otherwise process rest of input */
last = input + isize;
while (input < last)
asize = qpencode(*input++, atom, asize, marker, &buffer);
luaL_pushresult(&buffer);
lua_pushlstring(L, (char *) atom, asize);
return 2;
}
/*-------------------------------------------------------------------------*\
* Accumulate characters until we are sure about how to deal with them.
* Once we are sure, output the to the buffer, in the correct form.
\*-------------------------------------------------------------------------*/
static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) {
int d;
input[size++] = c;
/* deal with all characters we can deal */
switch (input[0]) {
/* if we have an escape character */
case '=':
if (size < 3) return size;
/* eliminate soft line break */
if (input[1] == '\r' && input[2] == '\n') return 0;
/* decode quoted representation */
c = qpunbase[input[1]]; d = qpunbase[input[2]];
/* if it is an invalid, do not decode */
if (c > 15 || d > 15) luaL_addlstring(buffer, (char *)input, 3);
else luaL_addchar(buffer, (c << 4) + d);
return 0;
case '\r':
if (size < 2) return size;
if (input[1] == '\n') luaL_addlstring(buffer, (char *)input, 2);
return 0;
default:
if (input[0] == '\t' || (input[0] > 31 && input[0] < 127))
luaL_addchar(buffer, input[0]);
return 0;
}
}
/*-------------------------------------------------------------------------*\
* Incrementally decodes a string in quoted-printable
* A, B = qp(C, D)
* A is the decoded version of the largest prefix of C .. D that
* can be decoded without doubts.
* B has the remaining bytes of C .. D, *without* decoding.
\*-------------------------------------------------------------------------*/
static int mime_global_unqp(lua_State *L)
{
size_t asize = 0, isize = 0;
UC atom[3];
const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize);
const UC *last = input + isize;
luaL_Buffer buffer;
/* end-of-input blackhole */
if (!input) {
lua_pushnil(L);
lua_pushnil(L);
return 2;
}
/* process first part of input */
luaL_buffinit(L, &buffer);
while (input < last)
asize = qpdecode(*input++, atom, asize, &buffer);
input = (UC *) luaL_optlstring(L, 2, NULL, &isize);
/* if second part is nil, we are done */
if (!input) {
luaL_pushresult(&buffer);
if (!(*lua_tostring(L, -1))) lua_pushnil(L);
lua_pushnil(L);
return 2;
}
/* otherwise process rest of input */
last = input + isize;
while (input < last)
asize = qpdecode(*input++, atom, asize, &buffer);
luaL_pushresult(&buffer);
lua_pushlstring(L, (char *) atom, asize);
return 2;
}
/*-------------------------------------------------------------------------*\
* Incrementally breaks a quoted-printed string into lines
* A, n = qpwrp(l, B, length)
* A is a copy of B, broken into lines of at most 'length' bytes.
* 'l' is how many bytes are left for the first line of B.
* 'n' is the number of bytes left in the last line of A.
* There are two complications: lines can't be broken in the middle
* of an encoded =XX, and there might be line breaks already
\*-------------------------------------------------------------------------*/
static int mime_global_qpwrp(lua_State *L)
{
size_t size = 0;
int left = (int) luaL_checknumber(L, 1);
const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &size);
const UC *last = input + size;
int length = (int) luaL_optnumber(L, 3, 76);
luaL_Buffer buffer;
/* end-of-input blackhole */
if (!input) {
if (left < length) lua_pushstring(L, EQCRLF);
else lua_pushnil(L);
lua_pushnumber(L, length);
return 2;
}
/* process all input */
luaL_buffinit(L, &buffer);
while (input < last) {
switch (*input) {
case '\r':
break;
case '\n':
left = length;
luaL_addstring(&buffer, CRLF);
break;
case '=':
if (left <= 3) {
left = length;
luaL_addstring(&buffer, EQCRLF);
}
luaL_addchar(&buffer, *input);
left--;
break;
default:
if (left <= 1) {
left = length;
luaL_addstring(&buffer, EQCRLF);
}
luaL_addchar(&buffer, *input);
left--;
break;
}
input++;
}
luaL_pushresult(&buffer);
lua_pushnumber(L, left);
return 2;
}
/*-------------------------------------------------------------------------*\
* Here is what we do: \n, and \r are considered candidates for line
* break. We issue *one* new line marker if any of them is seen alone, or
* followed by a different one. That is, \n\n and \r\r will issue two
* end of line markers each, but \r\n, \n\r etc will only issue *one*
* marker. This covers Mac OS, Mac OS X, VMS, Unix and DOS, as well as
* probably other more obscure conventions.
*
* c is the current character being processed
* last is the previous character
\*-------------------------------------------------------------------------*/
#define eolcandidate(c) (c == '\r' || c == '\n')
static int eolprocess(int c, int last, const char *marker,
luaL_Buffer *buffer)
{
if (eolcandidate(c)) {
if (eolcandidate(last)) {
if (c == last) luaL_addstring(buffer, marker);
return 0;
} else {
luaL_addstring(buffer, marker);
return c;
}
} else {
luaL_addchar(buffer, c);
return 0;
}
}
/*-------------------------------------------------------------------------*\
* Converts a string to uniform EOL convention.
* A, n = eol(o, B, marker)
* A is the converted version of the largest prefix of B that can be
* converted unambiguously. 'o' is the context returned by the previous
* call. 'n' is the new context.
\*-------------------------------------------------------------------------*/
static int mime_global_eol(lua_State *L)
{
int ctx = luaL_checkint(L, 1);
size_t isize = 0;
const char *input = luaL_optlstring(L, 2, NULL, &isize);
const char *last = input + isize;
const char *marker = luaL_optstring(L, 3, CRLF);
luaL_Buffer buffer;
luaL_buffinit(L, &buffer);
/* end of input blackhole */
if (!input) {
lua_pushnil(L);
lua_pushnumber(L, 0);
return 2;
}
/* process all input */
while (input < last)
ctx = eolprocess(*input++, ctx, marker, &buffer);
luaL_pushresult(&buffer);
lua_pushnumber(L, ctx);
return 2;
}
/*-------------------------------------------------------------------------*\
* Takes one byte and stuff it if needed.
\*-------------------------------------------------------------------------*/
static size_t dot(int c, size_t state, luaL_Buffer *buffer)
{
luaL_addchar(buffer, c);
switch (c) {
case '\r':
return 1;
case '\n':
return (state == 1)? 2: 0;
case '.':
if (state == 2)
luaL_addchar(buffer, '.');
default:
return 0;
}
}
/*-------------------------------------------------------------------------*\
* Incrementally applies smtp stuffing to a string
* A, n = dot(l, D)
\*-------------------------------------------------------------------------*/
static int mime_global_dot(lua_State *L)
{
size_t isize = 0, state = (size_t) luaL_checknumber(L, 1);
const char *input = luaL_optlstring(L, 2, NULL, &isize);
const char *last = input + isize;
luaL_Buffer buffer;
/* end-of-input blackhole */
if (!input) {
lua_pushnil(L);
lua_pushnumber(L, 2);
return 2;
}
/* process all input */
luaL_buffinit(L, &buffer);
while (input < last)
state = dot(*input++, state, &buffer);
luaL_pushresult(&buffer);
lua_pushnumber(L, state);
return 2;
}

29
src/mime.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef MIME_H
#define MIME_H
/*=========================================================================*\
* Core MIME support
* LuaSocket toolkit
*
* This module provides functions to implement transfer content encodings
* and formatting conforming to RFC 2045. It is used by mime.lua, which
* provide a higher level interface to this functionality.
\*=========================================================================*/
#include "lua.h"
/*-------------------------------------------------------------------------*\
* Current MIME library version
\*-------------------------------------------------------------------------*/
#define MIME_VERSION "MIME 1.0.3"
#define MIME_COPYRIGHT "Copyright (C) 2004-2009 Diego Nehab"
#define MIME_AUTHORS "Diego Nehab"
/*-------------------------------------------------------------------------*\
* This macro prefixes all exported API functions
\*-------------------------------------------------------------------------*/
#ifndef MIME_API
#define MIME_API extern
#endif
MIME_API int luaopen_mime_core(lua_State *L);
#endif /* MIME_H */

87
src/mime.lua Normal file
View File

@ -0,0 +1,87 @@
-----------------------------------------------------------------------------
-- MIME support for the Lua language.
-- Author: Diego Nehab
-- Conforming to RFCs 2045-2049
-- RCS ID: $Id: mime.lua,v 1.29 2007/06/11 23:44:54 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local ltn12 = require("ltn12")
local mime = require("mime.core")
local io = require("io")
local string = require("string")
module("mime")
-- encode, decode and wrap algorithm tables
encodet = {}
decodet = {}
wrapt = {}
-- creates a function that chooses a filter by name from a given table
local function choose(table)
return function(name, opt1, opt2)
if base.type(name) ~= "string" then
name, opt1, opt2 = "default", name, opt1
end
local f = table[name or "nil"]
if not f then
base.error("unknown key (" .. base.tostring(name) .. ")", 3)
else return f(opt1, opt2) end
end
end
-- define the encoding filters
encodet['base64'] = function()
return ltn12.filter.cycle(b64, "")
end
encodet['quoted-printable'] = function(mode)
return ltn12.filter.cycle(qp, "",
(mode == "binary") and "=0D=0A" or "\r\n")
end
-- define the decoding filters
decodet['base64'] = function()
return ltn12.filter.cycle(unb64, "")
end
decodet['quoted-printable'] = function()
return ltn12.filter.cycle(unqp, "")
end
local function format(chunk)
if chunk then
if chunk == "" then return "''"
else return string.len(chunk) end
else return "nil" end
end
-- define the line-wrap filters
wrapt['text'] = function(length)
length = length or 76
return ltn12.filter.cycle(wrp, length, length)
end
wrapt['base64'] = wrapt['text']
wrapt['default'] = wrapt['text']
wrapt['quoted-printable'] = function()
return ltn12.filter.cycle(qpwrp, 76, 76)
end
-- function that choose the encoding, decoding or wrap algorithm
encode = choose(encodet)
decode = choose(decodet)
wrap = choose(wrapt)
-- define the end-of-line normalization filter
function normalize(marker)
return ltn12.filter.cycle(eol, 0, marker)
end
-- high level stuffing filter
function stuff()
return ltn12.filter.cycle(dot, 2)
end

217
src/options.c Normal file
View File

@ -0,0 +1,217 @@
/*=========================================================================*\
* Common option interface
* LuaSocket toolkit
*
* RCS ID: $Id: options.c,v 1.7 2009/05/27 09:31:35 diego Exp $
\*=========================================================================*/
#include <string.h>
#include "lauxlib.h"
#include "auxiliar.h"
#include "options.h"
#include "inet.h"
#include "lua_typeerror.h"
/*=========================================================================*\
* Internal functions prototypes
\*=========================================================================*/
static int opt_setmembership(lua_State *L, p_socket ps, int level, int name);
static int opt_setboolean(lua_State *L, p_socket ps, int level, int name);
static int opt_getboolean(lua_State *L, p_socket ps, int level, int name);
static int opt_set(lua_State *L, p_socket ps, int level, int name,
void *val, int len);
/*=========================================================================*\
* Exported functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Calls appropriate option handler
\*-------------------------------------------------------------------------*/
int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps)
{
const char *name = luaL_checkstring(L, 2); /* obj, name, ... */
while (opt->name && strcmp(name, opt->name))
opt++;
if (!opt->func) {
char msg[45];
sprintf(msg, "unsupported option `%.35s'", name);
luaL_argerror(L, 2, msg);
}
return opt->func(L, ps);
}
int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps)
{
const char *name = luaL_checkstring(L, 2); /* obj, name, ... */
while (opt->name && strcmp(name, opt->name))
opt++;
if (!opt->func) {
char msg[45];
sprintf(msg, "unsupported option `%.35s'", name);
luaL_argerror(L, 2, msg);
}
return opt->func(L, ps);
}
/* enables reuse of local address */
int opt_set_reuseaddr(lua_State *L, p_socket ps)
{
return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR);
}
/* enables reuse of local port */
int opt_set_reuseport(lua_State *L, p_socket ps)
{
return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEPORT);
}
/* disables the Naggle algorithm */
int opt_set_tcp_nodelay(lua_State *L, p_socket ps)
{
return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY);
}
int opt_set_keepalive(lua_State *L, p_socket ps)
{
return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE);
}
int opt_set_dontroute(lua_State *L, p_socket ps)
{
return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE);
}
int opt_set_broadcast(lua_State *L, p_socket ps)
{
return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST);
}
int opt_set_ip_multicast_loop(lua_State *L, p_socket ps)
{
return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP);
}
int opt_get_ip_multicast_loop(lua_State *L, p_socket ps)
{
return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP);
}
int opt_set_linger(lua_State *L, p_socket ps)
{
struct linger li; /* obj, name, table */
if (!lua_istable(L, 3)) luaL_typeerror(L, 3, lua_typename(L, LUA_TTABLE));
lua_pushstring(L, "on");
lua_gettable(L, 3);
if (!lua_isboolean(L, -1))
luaL_argerror(L, 3, "boolean 'on' field expected");
li.l_onoff = (u_short) lua_toboolean(L, -1);
lua_pushstring(L, "timeout");
lua_gettable(L, 3);
if (!lua_isnumber(L, -1))
luaL_argerror(L, 3, "number 'timeout' field expected");
li.l_linger = (u_short) lua_tonumber(L, -1);
return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));
}
int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps)
{
int val = (int) luaL_checknumber(L, 3); /* obj, name, int */
return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_TTL,
(char *) &val, sizeof(val));
}
int opt_set_ip_multicast_if(lua_State *L, p_socket ps)
{
const char *address = luaL_checkstring(L, 3); /* obj, name, ip */
struct in_addr val;
val.s_addr = htonl(INADDR_ANY);
if (strcmp(address, "*") && !inet_aton(address, &val))
luaL_argerror(L, 3, "ip expected");
return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF,
(char *) &val, sizeof(val));
}
int opt_get_ip_multicast_if(lua_State *L, p_socket ps)
{
struct in_addr val;
socklen_t len = sizeof(val);
if (getsockopt(*ps, IPPROTO_IP, IP_MULTICAST_IF, (char *) &val, &len) < 0) {
lua_pushnil(L);
lua_pushstring(L, "getsockopt failed");
return 2;
}
lua_pushstring(L, inet_ntoa(val));
return 1;
}
int opt_set_ip_add_membership(lua_State *L, p_socket ps)
{
return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP);
}
int opt_set_ip_drop_membersip(lua_State *L, p_socket ps)
{
return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP);
}
int opt_set_ip6_v6only(lua_State *L, p_socket ps)
{
return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY);
}
/*=========================================================================*\
* Auxiliar functions
\*=========================================================================*/
static int opt_setmembership(lua_State *L, p_socket ps, int level, int name)
{
struct ip_mreq val; /* obj, name, table */
if (!lua_istable(L, 3)) luaL_typeerror(L, 3, lua_typename(L, LUA_TTABLE));
lua_pushstring(L, "multiaddr");
lua_gettable(L, 3);
if (!lua_isstring(L, -1))
luaL_argerror(L, 3, "string 'multiaddr' field expected");
if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr))
luaL_argerror(L, 3, "invalid 'multiaddr' ip address");
lua_pushstring(L, "interface");
lua_gettable(L, 3);
if (!lua_isstring(L, -1))
luaL_argerror(L, 3, "string 'interface' field expected");
val.imr_interface.s_addr = htonl(INADDR_ANY);
if (strcmp(lua_tostring(L, -1), "*") &&
!inet_aton(lua_tostring(L, -1), &val.imr_interface))
luaL_argerror(L, 3, "invalid 'interface' ip address");
return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
}
static
int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len)
{
if (setsockopt(*ps, level, name, (char *) val, len) < 0) {
lua_pushnil(L);
lua_pushstring(L, "setsockopt failed");
return 2;
}
lua_pushnumber(L, 1);
return 1;
}
static int opt_getboolean(lua_State *L, p_socket ps, int level, int name)
{
int val = 0;
socklen_t len = sizeof(val);
if (getsockopt(*ps, level, name, (char *) &val, &len) < 0) {
lua_pushnil(L);
lua_pushstring(L, "getsockopt failed");
return 2;
}
lua_pushboolean(L, val);
return 1;
}
static int opt_setboolean(lua_State *L, p_socket ps, int level, int name)
{
int val = auxiliar_checkboolean(L, 3); /* obj, name, bool */
return opt_set(L, ps, level, name, (char *) &val, sizeof(val));
}

46
src/options.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef OPTIONS_H
#define OPTIONS_H
/*=========================================================================*\
* Common option interface
* LuaSocket toolkit
*
* This module provides a common interface to socket options, used mainly by
* modules UDP and TCP.
\*=========================================================================*/
#include "lua.h"
#include "socket.h"
/* option registry */
typedef struct t_opt {
const char *name;
int (*func)(lua_State *L, p_socket ps);
} t_opt;
typedef t_opt *p_opt;
/* supported options for setoption */
int opt_set_dontroute(lua_State *L, p_socket ps);
int opt_set_broadcast(lua_State *L, p_socket ps);
int opt_set_reuseaddr(lua_State *L, p_socket ps);
int opt_set_tcp_nodelay(lua_State *L, p_socket ps);
int opt_set_keepalive(lua_State *L, p_socket ps);
int opt_set_linger(lua_State *L, p_socket ps);
int opt_set_reuseaddr(lua_State *L, p_socket ps);
int opt_set_reuseport(lua_State *L, p_socket ps);
int opt_set_ip_multicast_if(lua_State *L, p_socket ps);
int opt_set_ip_multicast_ttl(lua_State *L, p_socket ps);
int opt_set_ip_multicast_loop(lua_State *L, p_socket ps);
int opt_set_ip_add_membership(lua_State *L, p_socket ps);
int opt_set_ip_drop_membersip(lua_State *L, p_socket ps);
int opt_set_ip6_v6only(lua_State *L, p_socket ps);
/* invokes the appropriate option handler */
int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps);
/* supported options for getoption */
int opt_get_ip_multicast_loop(lua_State *L, p_socket ps);
int opt_get_ip_multicast_if(lua_State *L, p_socket ps);
/* invokes the appropriate option handler */
int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps);
#endif

BIN
src/options.o Normal file

Binary file not shown.

218
src/select.c Normal file
View File

@ -0,0 +1,218 @@
/*=========================================================================*\
* Select implementation
* LuaSocket toolkit
*
* RCS ID: $Id: select.c,v 1.23 2009/05/27 09:31:35 diego Exp $
\*=========================================================================*/
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "socket.h"
#include "timeout.h"
#include "select.h"
/*=========================================================================*\
* Internal function prototypes.
\*=========================================================================*/
static t_socket getfd(lua_State *L);
static int dirty(lua_State *L);
static void collect_fd(lua_State *L, int tab, int itab,
fd_set *set, t_socket *max_fd);
static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set);
static void return_fd(lua_State *L, fd_set *set, t_socket max_fd,
int itab, int tab, int start);
static void make_assoc(lua_State *L, int tab);
static int global_select(lua_State *L);
/* functions in library namespace */
static luaL_Reg func[] = {
{"select", global_select},
{NULL, NULL}
};
/*=========================================================================*\
* Exported functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int select_open(lua_State *L) {
lua_pushstring(L, "_SETSIZE");
lua_pushnumber(L, FD_SETSIZE);
lua_rawset(L, -3);
luaL_openlib(L, NULL, func, 0);
return 0;
}
/*=========================================================================*\
* Global Lua functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Waits for a set of sockets until a condition is met or timeout.
\*-------------------------------------------------------------------------*/
static int global_select(lua_State *L) {
int rtab, wtab, itab, ret, ndirty;
t_socket max_fd = SOCKET_INVALID;
fd_set rset, wset;
t_timeout tm;
double t = luaL_optnumber(L, 3, -1);
FD_ZERO(&rset); FD_ZERO(&wset);
lua_settop(L, 3);
lua_newtable(L); itab = lua_gettop(L);
lua_newtable(L); rtab = lua_gettop(L);
lua_newtable(L); wtab = lua_gettop(L);
collect_fd(L, 1, itab, &rset, &max_fd);
collect_fd(L, 2, itab, &wset, &max_fd);
ndirty = check_dirty(L, 1, rtab, &rset);
t = ndirty > 0? 0.0: t;
timeout_init(&tm, t, -1);
timeout_markstart(&tm);
ret = socket_select(max_fd+1, &rset, &wset, NULL, &tm);
if (ret > 0 || ndirty > 0) {
return_fd(L, &rset, max_fd+1, itab, rtab, ndirty);
return_fd(L, &wset, max_fd+1, itab, wtab, 0);
make_assoc(L, rtab);
make_assoc(L, wtab);
return 2;
} else if (ret == 0) {
lua_pushstring(L, "timeout");
return 3;
} else {
luaL_error(L, "select failed");
return 3;
}
}
/*=========================================================================*\
* Internal functions
\*=========================================================================*/
static t_socket getfd(lua_State *L) {
t_socket fd = SOCKET_INVALID;
lua_pushstring(L, "getfd");
lua_gettable(L, -2);
if (!lua_isnil(L, -1)) {
lua_pushvalue(L, -2);
lua_call(L, 1, 1);
if (lua_isnumber(L, -1)) {
double numfd = lua_tonumber(L, -1);
fd = (numfd >= 0.0)? (t_socket) numfd: SOCKET_INVALID;
}
}
lua_pop(L, 1);
return fd;
}
static int dirty(lua_State *L) {
int is = 0;
lua_pushstring(L, "dirty");
lua_gettable(L, -2);
if (!lua_isnil(L, -1)) {
lua_pushvalue(L, -2);
lua_call(L, 1, 1);
is = lua_toboolean(L, -1);
}
lua_pop(L, 1);
return is;
}
static void collect_fd(lua_State *L, int tab, int itab,
fd_set *set, t_socket *max_fd) {
int i = 1, n = 0;
/* nil is the same as an empty table */
if (lua_isnil(L, tab)) return;
/* otherwise we need it to be a table */
luaL_checktype(L, tab, LUA_TTABLE);
while (1) {
t_socket fd;
lua_pushnumber(L, i);
lua_gettable(L, tab);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
break;
}
/* getfd figures out if this is a socket */
fd = getfd(L);
if (fd != SOCKET_INVALID) {
/* make sure we don't overflow the fd_set */
#ifdef _WIN32
if (n >= FD_SETSIZE)
luaL_argerror(L, tab, "too many sockets");
#else
if (fd >= FD_SETSIZE)
luaL_argerror(L, tab, "descriptor too large for set size");
#endif
FD_SET(fd, set);
n++;
/* keep track of the largest descriptor so far */
if (*max_fd == SOCKET_INVALID || *max_fd < fd)
*max_fd = fd;
/* make sure we can map back from descriptor to the object */
lua_pushnumber(L, fd);
lua_pushvalue(L, -2);
lua_settable(L, itab);
}
lua_pop(L, 1);
i = i + 1;
}
}
static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) {
int ndirty = 0, i = 1;
if (lua_isnil(L, tab))
return 0;
while (1) {
t_socket fd;
lua_pushnumber(L, i);
lua_gettable(L, tab);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
break;
}
fd = getfd(L);
if (fd != SOCKET_INVALID && dirty(L)) {
lua_pushnumber(L, ++ndirty);
lua_pushvalue(L, -2);
lua_settable(L, dtab);
FD_CLR(fd, set);
}
lua_pop(L, 1);
i = i + 1;
}
return ndirty;
}
static void return_fd(lua_State *L, fd_set *set, t_socket max_fd,
int itab, int tab, int start) {
t_socket fd;
for (fd = 0; fd < max_fd; fd++) {
if (FD_ISSET(fd, set)) {
lua_pushnumber(L, ++start);
lua_pushnumber(L, fd);
lua_gettable(L, itab);
lua_settable(L, tab);
}
}
}
static void make_assoc(lua_State *L, int tab) {
int i = 1, atab;
lua_newtable(L); atab = lua_gettop(L);
while (1) {
lua_pushnumber(L, i);
lua_gettable(L, tab);
if (!lua_isnil(L, -1)) {
lua_pushnumber(L, i);
lua_pushvalue(L, -2);
lua_settable(L, atab);
lua_pushnumber(L, i);
lua_settable(L, atab);
} else {
lua_pop(L, 1);
break;
}
i = i+1;
}
}

15
src/select.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef SELECT_H
#define SELECT_H
/*=========================================================================*\
* Select implementation
* LuaSocket toolkit
*
* Each object that can be passed to the select function has to export
* method getfd() which returns the descriptor to be passed to the
* underlying select function. Another method, dirty(), should return
* true if there is data ready for reading (required for buffered input).
\*=========================================================================*/
int select_open(lua_State *L);
#endif /* SELECT_H */

BIN
src/select.o Normal file

Binary file not shown.

253
src/smtp.lua Normal file
View File

@ -0,0 +1,253 @@
-----------------------------------------------------------------------------
-- SMTP client support for the Lua language.
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: smtp.lua,v 1.47 2009/05/27 09:31:35 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local coroutine = require("coroutine")
local string = require("string")
local math = require("math")
local os = require("os")
local socket = require("socket")
local tp = require("socket.tp")
local ltn12 = require("ltn12")
local headers = require("socket.headers")
local mime = require("mime")
module("socket.smtp")
-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
-- timeout for connection
TIMEOUT = 60
-- default server used to send e-mails
SERVER = "localhost"
-- default port
PORT = 25
-- domain used in HELO command and default sendmail
-- If we are under a CGI, try to get from environment
DOMAIN = os.getenv("SERVER_NAME") or "localhost"
-- default time zone (means we don't know)
ZONE = "-0000"
---------------------------------------------------------------------------
-- Low level SMTP API
-----------------------------------------------------------------------------
local metat = { __index = {} }
function metat.__index:greet(domain)
self.try(self.tp:check("2.."))
self.try(self.tp:command("EHLO", domain or DOMAIN))
return socket.skip(1, self.try(self.tp:check("2..")))
end
function metat.__index:mail(from)
self.try(self.tp:command("MAIL", "FROM:" .. from))
return self.try(self.tp:check("2.."))
end
function metat.__index:rcpt(to)
self.try(self.tp:command("RCPT", "TO:" .. to))
return self.try(self.tp:check("2.."))
end
function metat.__index:data(src, step)
self.try(self.tp:command("DATA"))
self.try(self.tp:check("3.."))
self.try(self.tp:source(src, step))
self.try(self.tp:send("\r\n.\r\n"))
return self.try(self.tp:check("2.."))
end
function metat.__index:quit()
self.try(self.tp:command("QUIT"))
return self.try(self.tp:check("2.."))
end
function metat.__index:close()
return self.tp:close()
end
function metat.__index:login(user, password)
self.try(self.tp:command("AUTH", "LOGIN"))
self.try(self.tp:check("3.."))
self.try(self.tp:command(mime.b64(user)))
self.try(self.tp:check("3.."))
self.try(self.tp:command(mime.b64(password)))
return self.try(self.tp:check("2.."))
end
function metat.__index:plain(user, password)
local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password)
self.try(self.tp:command("AUTH", auth))
return self.try(self.tp:check("2.."))
end
function metat.__index:auth(user, password, ext)
if not user or not password then return 1 end
if string.find(ext, "AUTH[^\n]+LOGIN") then
return self:login(user, password)
elseif string.find(ext, "AUTH[^\n]+PLAIN") then
return self:plain(user, password)
else
self.try(nil, "authentication not supported")
end
end
-- send message or throw an exception
function metat.__index:send(mailt)
self:mail(mailt.from)
if base.type(mailt.rcpt) == "table" then
for i,v in base.ipairs(mailt.rcpt) do
self:rcpt(v)
end
else
self:rcpt(mailt.rcpt)
end
self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step)
end
function open(server, port, create)
local tp = socket.try(tp.connect(server or SERVER, port or PORT,
TIMEOUT, create))
local s = base.setmetatable({tp = tp}, metat)
-- make sure tp is closed if we get an exception
s.try = socket.newtry(function()
s:close()
end)
return s
end
-- convert headers to lowercase
local function lower_headers(headers)
local lower = {}
for i,v in base.pairs(headers or lower) do
lower[string.lower(i)] = v
end
return lower
end
---------------------------------------------------------------------------
-- Multipart message source
-----------------------------------------------------------------------------
-- returns a hopefully unique mime boundary
local seqno = 0
local function newboundary()
seqno = seqno + 1
return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'),
math.random(0, 99999), seqno)
end
-- send_message forward declaration
local send_message
-- yield the headers all at once, it's faster
local function send_headers(tosend)
local canonic = headers.canonic
local h = "\r\n"
for f,v in base.pairs(tosend) do
h = (canonic[f] or f) .. ': ' .. v .. "\r\n" .. h
end
coroutine.yield(h)
end
-- yield multipart message body from a multipart message table
local function send_multipart(mesgt)
-- make sure we have our boundary and send headers
local bd = newboundary()
local headers = lower_headers(mesgt.headers or {})
headers['content-type'] = headers['content-type'] or 'multipart/mixed'
headers['content-type'] = headers['content-type'] ..
'; boundary="' .. bd .. '"'
send_headers(headers)
-- send preamble
if mesgt.body.preamble then
coroutine.yield(mesgt.body.preamble)
coroutine.yield("\r\n")
end
-- send each part separated by a boundary
for i, m in base.ipairs(mesgt.body) do
coroutine.yield("\r\n--" .. bd .. "\r\n")
send_message(m)
end
-- send last boundary
coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n")
-- send epilogue
if mesgt.body.epilogue then
coroutine.yield(mesgt.body.epilogue)
coroutine.yield("\r\n")
end
end
-- yield message body from a source
local function send_source(mesgt)
-- make sure we have a content-type
local headers = lower_headers(mesgt.headers or {})
headers['content-type'] = headers['content-type'] or
'text/plain; charset="iso-8859-1"'
send_headers(headers)
-- send body from source
while true do
local chunk, err = mesgt.body()
if err then coroutine.yield(nil, err)
elseif chunk then coroutine.yield(chunk)
else break end
end
end
-- yield message body from a string
local function send_string(mesgt)
-- make sure we have a content-type
local headers = lower_headers(mesgt.headers or {})
headers['content-type'] = headers['content-type'] or
'text/plain; charset="iso-8859-1"'
send_headers(headers)
-- send body from string
coroutine.yield(mesgt.body)
end
-- message source
function send_message(mesgt)
if base.type(mesgt.body) == "table" then send_multipart(mesgt)
elseif base.type(mesgt.body) == "function" then send_source(mesgt)
else send_string(mesgt) end
end
-- set defaul headers
local function adjust_headers(mesgt)
local lower = lower_headers(mesgt.headers)
lower["date"] = lower["date"] or
os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or ZONE)
lower["x-mailer"] = lower["x-mailer"] or socket._VERSION
-- this can't be overriden
lower["mime-version"] = "1.0"
return lower
end
function message(mesgt)
mesgt.headers = adjust_headers(mesgt)
-- create and return message source
local co = coroutine.create(function() send_message(mesgt) end)
return function()
local ret, a, b = coroutine.resume(co)
if ret then return a, b
else return nil, a end
end
end
---------------------------------------------------------------------------
-- High level SMTP API
-----------------------------------------------------------------------------
send = socket.protect(function(mailt)
local s = open(mailt.server, mailt.port, mailt.create)
local ext = s:greet(mailt.domain)
s:auth(mailt.user, mailt.password, ext)
s:send(mailt)
s:quit()
return s:close()
end)

75
src/socket.h Normal file
View File

@ -0,0 +1,75 @@
#ifndef SOCKET_H
#define SOCKET_H
/*=========================================================================*\
* Socket compatibilization module
* LuaSocket toolkit
*
* BSD Sockets and WinSock are similar, but there are a few irritating
* differences. Also, not all *nix platforms behave the same. This module
* (and the associated usocket.h and wsocket.h) factor these differences and
* creates a interface compatible with the io.h module.
\*=========================================================================*/
#include "io.h"
/*=========================================================================*\
* Platform specific compatibilization
\*=========================================================================*/
#ifdef _WIN32
#include "wsocket.h"
#else
#include "usocket.h"
#endif
/*=========================================================================*\
* The connect and accept functions accept a timeout and their
* implementations are somewhat complicated. We chose to move
* the timeout control into this module for these functions in
* order to simplify the modules that use them.
\*=========================================================================*/
#include "timeout.h"
/* we are lazy... */
typedef struct sockaddr SA;
/*=========================================================================*\
* Functions bellow implement a comfortable platform independent
* interface to sockets
\*=========================================================================*/
int socket_open(void);
int socket_close(void);
void socket_destroy(p_socket ps);
void socket_shutdown(p_socket ps, int how);
int socket_sendto(p_socket ps, const char *data, size_t count,
size_t *sent, SA *addr, socklen_t addr_len, p_timeout tm);
int socket_recvfrom(p_socket ps, char *data, size_t count,
size_t *got, SA *addr, socklen_t *addr_len, p_timeout tm);
void socket_setnonblocking(p_socket ps);
void socket_setblocking(p_socket ps);
int socket_waitfd(p_socket ps, int sw, p_timeout tm);
int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds,
p_timeout tm);
int socket_connect(p_socket ps, SA *addr, socklen_t addr_len, p_timeout tm);
int socket_create(p_socket ps, int domain, int type, int protocol);
int socket_bind(p_socket ps, SA *addr, socklen_t addr_len);
int socket_listen(p_socket ps, int backlog);
int socket_accept(p_socket ps, p_socket pa, SA *addr,
socklen_t *addr_len, p_timeout tm);
const char *socket_hoststrerror(int err);
const char *socket_gaistrerror(int err);
const char *socket_strerror(int err);
/* these are perfect to use with the io abstraction module
and the buffered input module */
int socket_send(p_socket ps, const char *data, size_t count,
size_t *sent, p_timeout tm);
int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm);
const char *socket_ioerror(p_socket ps, int err);
int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp);
int socket_gethostbyname(const char *addr, struct hostent **hp);
#endif /* SOCKET_H */

150
src/socket.lua Normal file
View File

@ -0,0 +1,150 @@
-----------------------------------------------------------------------------
-- LuaSocket helper module
-- Author: Diego Nehab
-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local string = require("string")
local math = require("math")
local socket = require("socket.core")
module("socket")
-----------------------------------------------------------------------------
-- Exported auxiliar functions
-----------------------------------------------------------------------------
function connect(address, port, laddress, lport)
if address == "*" then address = "0.0.0.0" end
local addrinfo, err = socket.dns.getaddrinfo(address);
if not addrinfo then return nil, err end
local sock, err;
if addrinfo[1].family == "inet" then
sock, err = socket.tcp()
else
sock, err = socket.tcp6()
end
if not sock then return nil, err end
if laddress then
local res, err = sock:bind(laddress, lport, -1)
if not res then return nil, err end
end
local res, err = sock:connect(address, port)
if not res then return nil, err end
return sock
end
function bind(host, port, backlog)
if host == "*" then host = "0.0.0.0" end
local addrinfo, err = socket.dns.getaddrinfo(host);
if not addrinfo then return nil, err end
local sock, err;
if addrinfo[1].family == "inet" then
sock, err = socket.tcp()
else
sock, err = socket.tcp6()
end
if not sock then return nil, err end
sock:setoption("reuseaddr", true)
sock:setoption("ipv6-v6only", false)
local res, err = sock:bind(host, port)
if not res then return nil, err end
res, err = sock:listen(backlog)
if not res then return nil, err end
return sock
end
try = newtry()
function choose(table)
return function(name, opt1, opt2)
if base.type(name) ~= "string" then
name, opt1, opt2 = "default", name, opt1
end
local f = table[name or "nil"]
if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
else return f(opt1, opt2) end
end
end
-----------------------------------------------------------------------------
-- Socket sources and sinks, conforming to LTN12
-----------------------------------------------------------------------------
-- create namespaces inside LuaSocket namespace
sourcet = {}
sinkt = {}
BLOCKSIZE = 2048
sinkt["close-when-done"] = function(sock)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function(self, chunk, err)
if not chunk then
sock:close()
return 1
else return sock:send(chunk) end
end
})
end
sinkt["keep-open"] = function(sock)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function(self, chunk, err)
if chunk then return sock:send(chunk)
else return 1 end
end
})
end
sinkt["default"] = sinkt["keep-open"]
sink = choose(sinkt)
sourcet["by-length"] = function(sock, length)
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function()
if length <= 0 then return nil end
local size = math.min(socket.BLOCKSIZE, length)
local chunk, err = sock:receive(size)
if err then return nil, err end
length = length - string.len(chunk)
return chunk
end
})
end
sourcet["until-closed"] = function(sock)
local done
return base.setmetatable({
getfd = function() return sock:getfd() end,
dirty = function() return sock:dirty() end
}, {
__call = function()
if done then return nil end
local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
if not err then return chunk
elseif err == "closed" then
sock:close()
done = 1
return partial
else return nil, err end
end
})
end
sourcet["default"] = sourcet["until-closed"]
source = choose(sourcet)

451
src/tcp.c Normal file
View File

@ -0,0 +1,451 @@
/*=========================================================================*\
* TCP object
* LuaSocket toolkit
*
* RCS ID: $Id: tcp.c,v 1.42 2009/05/27 09:31:35 diego Exp $
\*=========================================================================*/
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "auxiliar.h"
#include "socket.h"
#include "inet.h"
#include "options.h"
#include "tcp.h"
/*=========================================================================*\
* Internal function prototypes
\*=========================================================================*/
static int global_create(lua_State *L);
static int global_create6(lua_State *L);
static int global_connect6(lua_State *L);
static int meth_connect(lua_State *L);
static int meth_listen(lua_State *L);
static int meth_bind(lua_State *L);
static int meth_send(lua_State *L);
static int meth_getstats(lua_State *L);
static int meth_setstats(lua_State *L);
static int meth_getsockname(lua_State *L);
static int meth_getpeername(lua_State *L);
static int meth_shutdown(lua_State *L);
static int meth_receive(lua_State *L);
static int meth_accept(lua_State *L);
static int meth_close(lua_State *L);
static int meth_setoption(lua_State *L);
static int meth_settimeout(lua_State *L);
static int meth_getfd(lua_State *L);
static int meth_setfd(lua_State *L);
static int meth_dirty(lua_State *L);
/* tcp object methods */
static luaL_Reg tcp_methods[] = {
{"__gc", meth_close},
{"__tostring", auxiliar_tostring},
{"accept", meth_accept},
{"bind", meth_bind},
{"close", meth_close},
{"connect", meth_connect},
{"dirty", meth_dirty},
{"getfd", meth_getfd},
{"getpeername", meth_getpeername},
{"getsockname", meth_getsockname},
{"getstats", meth_getstats},
{"setstats", meth_setstats},
{"listen", meth_listen},
{"receive", meth_receive},
{"send", meth_send},
{"setfd", meth_setfd},
{"setoption", meth_setoption},
{"setpeername", meth_connect},
{"setsockname", meth_bind},
{"settimeout", meth_settimeout},
{"shutdown", meth_shutdown},
{NULL, NULL}
};
/* socket option handlers */
static t_opt optset[] = {
{"keepalive", opt_set_keepalive},
{"reuseaddr", opt_set_reuseaddr},
{"tcp-nodelay", opt_set_tcp_nodelay},
{"ipv6-v6only", opt_set_ip6_v6only},
{"linger", opt_set_linger},
{NULL, NULL}
};
/* functions in library namespace */
static luaL_Reg func[] = {
{"tcp", global_create},
{"tcp6", global_create6},
{"connect6", global_connect6},
{NULL, NULL}
};
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int tcp_open(lua_State *L)
{
/* create classes */
auxiliar_newclass(L, "tcp{master}", tcp_methods);
auxiliar_newclass(L, "tcp{client}", tcp_methods);
auxiliar_newclass(L, "tcp{server}", tcp_methods);
/* create class groups */
auxiliar_add2group(L, "tcp{master}", "tcp{any}");
auxiliar_add2group(L, "tcp{client}", "tcp{any}");
auxiliar_add2group(L, "tcp{server}", "tcp{any}");
/* define library functions */
luaL_openlib(L, NULL, func, 0);
return 0;
}
/*=========================================================================*\
* Lua methods
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Just call buffered IO methods
\*-------------------------------------------------------------------------*/
static int meth_send(lua_State *L) {
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1);
return buffer_meth_send(L, &tcp->buf);
}
static int meth_receive(lua_State *L) {
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1);
return buffer_meth_receive(L, &tcp->buf);
}
static int meth_getstats(lua_State *L) {
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1);
return buffer_meth_getstats(L, &tcp->buf);
}
static int meth_setstats(lua_State *L) {
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1);
return buffer_meth_setstats(L, &tcp->buf);
}
/*-------------------------------------------------------------------------*\
* Just call option handler
\*-------------------------------------------------------------------------*/
static int meth_setoption(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
return opt_meth_setoption(L, optset, &tcp->sock);
}
/*-------------------------------------------------------------------------*\
* Select support methods
\*-------------------------------------------------------------------------*/
static int meth_getfd(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
lua_pushnumber(L, (int) tcp->sock);
return 1;
}
/* this is very dangerous, but can be handy for those that are brave enough */
static int meth_setfd(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
tcp->sock = (t_socket) luaL_checknumber(L, 2);
return 0;
}
static int meth_dirty(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
lua_pushboolean(L, !buffer_isempty(&tcp->buf));
return 1;
}
/*-------------------------------------------------------------------------*\
* Waits for and returns a client object attempting connection to the
* server object
\*-------------------------------------------------------------------------*/
static int meth_accept(lua_State *L)
{
p_tcp server = (p_tcp) auxiliar_checkclass(L, "tcp{server}", 1);
p_timeout tm = timeout_markstart(&server->tm);
t_socket sock;
int err = socket_accept(&server->sock, &sock, NULL, NULL, tm);
/* if successful, push client socket */
if (err == IO_DONE) {
p_tcp clnt = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
auxiliar_setclass(L, "tcp{client}", -1);
/* initialize structure fields */
socket_setnonblocking(&sock);
clnt->sock = sock;
io_init(&clnt->io, (p_send) socket_send, (p_recv) socket_recv,
(p_error) socket_ioerror, &clnt->sock);
timeout_init(&clnt->tm, -1, -1);
buffer_init(&clnt->buf, &clnt->io, &clnt->tm);
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(err));
return 2;
}
}
/*-------------------------------------------------------------------------*\
* Binds an object to an address
\*-------------------------------------------------------------------------*/
static int meth_bind(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1);
const char *address = luaL_checkstring(L, 2);
const char *port = luaL_checkstring(L, 3);
const char *err;
struct addrinfo bindhints;
memset(&bindhints, 0, sizeof(bindhints));
bindhints.ai_socktype = SOCK_STREAM;
bindhints.ai_family = tcp->domain;
bindhints.ai_flags = AI_PASSIVE;
err = inet_trybind(&tcp->sock, address, port, &bindhints);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Turns a master tcp object into a client object.
\*-------------------------------------------------------------------------*/
static int meth_connect(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
const char *address = luaL_checkstring(L, 2);
const char *port = luaL_checkstring(L, 3);
struct addrinfo connecthints;
const char *err;
memset(&connecthints, 0, sizeof(connecthints));
connecthints.ai_socktype = SOCK_STREAM;
/* make sure we try to connect only to the same family */
connecthints.ai_family = tcp->domain;
timeout_markstart(&tcp->tm);
err = inet_tryconnect(&tcp->sock, address, port,
&tcp->tm, &connecthints);
/* have to set the class even if it failed due to non-blocking connects */
auxiliar_setclass(L, "tcp{client}", 1);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Closes socket used by object
\*-------------------------------------------------------------------------*/
static int meth_close(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
socket_destroy(&tcp->sock);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Puts the sockt in listen mode
\*-------------------------------------------------------------------------*/
static int meth_listen(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1);
int backlog = (int) luaL_optnumber(L, 2, 32);
int err = socket_listen(&tcp->sock, backlog);
if (err != IO_DONE) {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(err));
return 2;
}
/* turn master object into a server object */
auxiliar_setclass(L, "tcp{server}", 1);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Shuts the connection down partially
\*-------------------------------------------------------------------------*/
static int meth_shutdown(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1);
const char *how = luaL_optstring(L, 2, "both");
switch (how[0]) {
case 'b':
if (strcmp(how, "both")) goto error;
socket_shutdown(&tcp->sock, 2);
break;
case 's':
if (strcmp(how, "send")) goto error;
socket_shutdown(&tcp->sock, 1);
break;
case 'r':
if (strcmp(how, "receive")) goto error;
socket_shutdown(&tcp->sock, 0);
break;
}
lua_pushnumber(L, 1);
return 1;
error:
luaL_argerror(L, 2, "invalid shutdown method");
return 0;
}
/*-------------------------------------------------------------------------*\
* Just call inet methods
\*-------------------------------------------------------------------------*/
static int meth_getpeername(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
return inet_meth_getpeername(L, &tcp->sock);
}
static int meth_getsockname(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
return inet_meth_getsockname(L, &tcp->sock);
}
/*-------------------------------------------------------------------------*\
* Just call tm methods
\*-------------------------------------------------------------------------*/
static int meth_settimeout(lua_State *L)
{
p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1);
return timeout_meth_settimeout(L, &tcp->tm);
}
/*=========================================================================*\
* Library functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Creates a master tcp object
\*-------------------------------------------------------------------------*/
static int tcp_create(lua_State *L, int domain) {
t_socket sock;
const char *err = inet_trycreate(&sock, domain, SOCK_STREAM);
/* try to allocate a system socket */
if (!err) {
/* allocate tcp object */
p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
/* set its type as master object */
auxiliar_setclass(L, "tcp{master}", -1);
/* initialize remaining structure fields */
socket_setnonblocking(&sock);
if (domain == PF_INET6) {
int yes = 1;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
(void *)&yes, sizeof(yes));
}
tcp->sock = sock;
io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv,
(p_error) socket_ioerror, &tcp->sock);
timeout_init(&tcp->tm, -1, -1);
buffer_init(&tcp->buf, &tcp->io, &tcp->tm);
tcp->domain = domain;
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
}
static int global_create(lua_State *L) {
return tcp_create(L, AF_INET);
}
static int global_create6(lua_State *L) {
return tcp_create(L, AF_INET6);
}
static const char *tryconnect6(const char *remoteaddr, const char *remoteserv,
struct addrinfo *connecthints, p_tcp tcp) {
struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL;
/* try resolving */
err = socket_gaistrerror(getaddrinfo(remoteaddr, remoteserv,
connecthints, &resolved));
if (err != NULL) {
if (resolved) freeaddrinfo(resolved);
return err;
}
/* iterate over all returned addresses trying to connect */
for (iterator = resolved; iterator; iterator = iterator->ai_next) {
p_timeout tm = timeout_markstart(&tcp->tm);
/* create new socket if one wasn't created by the bind stage */
if (tcp->sock == SOCKET_INVALID) {
err = socket_strerror(socket_create(&tcp->sock,
iterator->ai_family, iterator->ai_socktype,
iterator->ai_protocol));
if (err != NULL) {
freeaddrinfo(resolved);
return err;
}
/* all sockets initially non-blocking */
socket_setnonblocking(&tcp->sock);
}
/* finally try connecting to remote address */
err = socket_strerror(socket_connect(&tcp->sock,
(SA *) iterator->ai_addr,
iterator->ai_addrlen, tm));
/* if success, break out of loop */
if (err == NULL) break;
}
freeaddrinfo(resolved);
/* here, if err is set, we failed */
return err;
}
static int global_connect6(lua_State *L) {
const char *remoteaddr = luaL_checkstring(L, 1);
const char *remoteserv = luaL_checkstring(L, 2);
const char *localaddr = luaL_optstring(L, 3, NULL);
const char *localserv = luaL_optstring(L, 4, "0");
p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
struct addrinfo bindhints, connecthints;
const char *err = NULL;
/* initialize tcp structure */
io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv,
(p_error) socket_ioerror, &tcp->sock);
timeout_init(&tcp->tm, -1, -1);
buffer_init(&tcp->buf, &tcp->io, &tcp->tm);
tcp->sock = SOCKET_INVALID;
/* allow user to pick local address and port */
memset(&bindhints, 0, sizeof(bindhints));
bindhints.ai_socktype = SOCK_STREAM;
bindhints.ai_family = PF_UNSPEC;
bindhints.ai_flags = AI_PASSIVE;
if (localaddr) {
err = inet_trybind(&tcp->sock, localaddr, localserv, &bindhints);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
}
/* try to connect to remote address and port */
memset(&connecthints, 0, sizeof(connecthints));
connecthints.ai_socktype = SOCK_STREAM;
/* make sure we try to connect only to the same family */
connecthints.ai_family = bindhints.ai_family;
err = tryconnect6(remoteaddr, remoteserv, &connecthints, tcp);
if (err) {
socket_destroy(&tcp->sock);
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
auxiliar_setclass(L, "tcp{client}", -1);
return 1;
}

35
src/tcp.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef TCP_H
#define TCP_H
/*=========================================================================*\
* TCP object
* LuaSocket toolkit
*
* The tcp.h module is basicly a glue that puts together modules buffer.h,
* timeout.h socket.h and inet.h to provide the LuaSocket TCP (AF_INET,
* SOCK_STREAM) support.
*
* Three classes are defined: master, client and server. The master class is
* a newly created tcp object, that has not been bound or connected. Server
* objects are tcp objects bound to some local address. Client objects are
* tcp objects either connected to some address or returned by the accept
* method of a server object.
\*=========================================================================*/
#include "lua.h"
#include "buffer.h"
#include "timeout.h"
#include "socket.h"
typedef struct t_tcp_ {
t_socket sock;
t_io io;
t_buffer buf;
t_timeout tm;
int domain;
} t_tcp;
typedef t_tcp *p_tcp;
int tcp_open(lua_State *L);
#endif /* TCP_H */

BIN
src/tcp.o Normal file

Binary file not shown.

219
src/timeout.c Normal file
View File

@ -0,0 +1,219 @@
/*=========================================================================*\
* Timeout management functions
* LuaSocket toolkit
*
* RCS ID: $Id: timeout.c,v 1.31 2009/05/27 09:31:35 diego Exp $
\*=========================================================================*/
#include <stdio.h>
#include <limits.h>
#include <float.h>
#include "lua.h"
#include "lauxlib.h"
#include "auxiliar.h"
#include "timeout.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <time.h>
#include <sys/time.h>
#endif
/* min and max macros */
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? x : y)
#endif
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? x : y)
#endif
/*=========================================================================*\
* Internal function prototypes
\*=========================================================================*/
static int timeout_lua_gettime(lua_State *L);
static int timeout_lua_sleep(lua_State *L);
static luaL_Reg func[] = {
{ "gettime", timeout_lua_gettime },
{ "sleep", timeout_lua_sleep },
{ NULL, NULL }
};
/*=========================================================================*\
* Exported functions.
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Initialize structure
\*-------------------------------------------------------------------------*/
void timeout_init(p_timeout tm, double block, double total) {
tm->block = block;
tm->total = total;
}
/*-------------------------------------------------------------------------*\
* Determines how much time we have left for the next system call,
* if the previous call was successful
* Input
* tm: timeout control structure
* Returns
* the number of ms left or -1 if there is no time limit
\*-------------------------------------------------------------------------*/
double timeout_get(p_timeout tm) {
if (tm->block < 0.0 && tm->total < 0.0) {
return -1;
} else if (tm->block < 0.0) {
double t = tm->total - timeout_gettime() + tm->start;
return MAX(t, 0.0);
} else if (tm->total < 0.0) {
return tm->block;
} else {
double t = tm->total - timeout_gettime() + tm->start;
return MIN(tm->block, MAX(t, 0.0));
}
}
/*-------------------------------------------------------------------------*\
* Returns time since start of operation
* Input
* tm: timeout control structure
* Returns
* start field of structure
\*-------------------------------------------------------------------------*/
double timeout_getstart(p_timeout tm) {
return tm->start;
}
/*-------------------------------------------------------------------------*\
* Determines how much time we have left for the next system call,
* if the previous call was a failure
* Input
* tm: timeout control structure
* Returns
* the number of ms left or -1 if there is no time limit
\*-------------------------------------------------------------------------*/
double timeout_getretry(p_timeout tm) {
if (tm->block < 0.0 && tm->total < 0.0) {
return -1;
} else if (tm->block < 0.0) {
double t = tm->total - timeout_gettime() + tm->start;
return MAX(t, 0.0);
} else if (tm->total < 0.0) {
double t = tm->block - timeout_gettime() + tm->start;
return MAX(t, 0.0);
} else {
double t = tm->total - timeout_gettime() + tm->start;
return MIN(tm->block, MAX(t, 0.0));
}
}
/*-------------------------------------------------------------------------*\
* Marks the operation start time in structure
* Input
* tm: timeout control structure
\*-------------------------------------------------------------------------*/
p_timeout timeout_markstart(p_timeout tm) {
tm->start = timeout_gettime();
return tm;
}
/*-------------------------------------------------------------------------*\
* Gets time in s, relative to January 1, 1970 (UTC)
* Returns
* time in s.
\*-------------------------------------------------------------------------*/
#ifdef _WIN32
double timeout_gettime(void) {
FILETIME ft;
double t;
GetSystemTimeAsFileTime(&ft);
/* Windows file time (time since January 1, 1601 (UTC)) */
t = ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7);
/* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */
return (t - 11644473600.0);
}
#else
double timeout_gettime(void) {
struct timeval v;
gettimeofday(&v, (struct timezone *) NULL);
/* Unix Epoch time (time since January 1, 1970 (UTC)) */
return v.tv_sec + v.tv_usec/1.0e6;
}
#endif
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int timeout_open(lua_State *L) {
luaL_openlib(L, NULL, func, 0);
return 0;
}
/*-------------------------------------------------------------------------*\
* Sets timeout values for IO operations
* Lua Input: base, time [, mode]
* time: time out value in seconds
* mode: "b" for block timeout, "t" for total timeout. (default: b)
\*-------------------------------------------------------------------------*/
int timeout_meth_settimeout(lua_State *L, p_timeout tm) {
double t = luaL_optnumber(L, 2, -1);
const char *mode = luaL_optstring(L, 3, "b");
switch (*mode) {
case 'b':
tm->block = t;
break;
case 'r': case 't':
tm->total = t;
break;
default:
luaL_argcheck(L, 0, 3, "invalid timeout mode");
break;
}
lua_pushnumber(L, 1);
return 1;
}
/*=========================================================================*\
* Test support functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Returns the time the system has been up, in secconds.
\*-------------------------------------------------------------------------*/
static int timeout_lua_gettime(lua_State *L)
{
lua_pushnumber(L, timeout_gettime());
return 1;
}
/*-------------------------------------------------------------------------*\
* Sleep for n seconds.
\*-------------------------------------------------------------------------*/
#ifdef _WIN32
int timeout_lua_sleep(lua_State *L)
{
double n = luaL_checknumber(L, 1);
if (n < 0.0) n = 0.0;
if (n < DBL_MAX/1000.0) n *= 1000.0;
if (n > INT_MAX) n = INT_MAX;
Sleep((int)n);
return 0;
}
#else
int timeout_lua_sleep(lua_State *L)
{
double n = luaL_checknumber(L, 1);
struct timespec t, r;
if (n < 0.0) n = 0.0;
if (n > INT_MAX) n = INT_MAX;
t.tv_sec = (int) n;
n -= t.tv_sec;
t.tv_nsec = (int) (n * 1000000000);
if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999;
while (nanosleep(&t, &r) != 0) {
t.tv_sec = r.tv_sec;
t.tv_nsec = r.tv_nsec;
}
return 0;
}
#endif

28
src/timeout.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef TIMEOUT_H
#define TIMEOUT_H
/*=========================================================================*\
* Timeout management functions
* LuaSocket toolkit
\*=========================================================================*/
#include "lua.h"
/* timeout control structure */
typedef struct t_timeout_ {
double block; /* maximum time for blocking calls */
double total; /* total number of miliseconds for operation */
double start; /* time of start of operation */
} t_timeout;
typedef t_timeout *p_timeout;
int timeout_open(lua_State *L);
void timeout_init(p_timeout tm, double block, double total);
double timeout_get(p_timeout tm);
double timeout_getretry(p_timeout tm);
p_timeout timeout_markstart(p_timeout tm);
double timeout_getstart(p_timeout tm);
double timeout_gettime(void);
int timeout_meth_settimeout(lua_State *L, p_timeout tm);
#define timeout_iszero(tm) ((tm)->block == 0.0)
#endif /* TIMEOUT_H */

BIN
src/timeout.o Normal file

Binary file not shown.

124
src/tp.lua Normal file
View File

@ -0,0 +1,124 @@
-----------------------------------------------------------------------------
-- Unified SMTP/FTP subsystem
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: tp.lua,v 1.23 2009/05/27 09:31:35 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local string = require("string")
local socket = require("socket")
local ltn12 = require("ltn12")
module("socket.tp")
-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
TIMEOUT = 60
-----------------------------------------------------------------------------
-- Implementation
-----------------------------------------------------------------------------
-- gets server reply (works for SMTP and FTP)
local function get_reply(c)
local code, current, sep
local line, err = c:receive()
local reply = line
if err then return nil, err end
code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
if not code then return nil, "invalid server reply" end
if sep == "-" then -- reply is multiline
repeat
line, err = c:receive()
if err then return nil, err end
current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
reply = reply .. "\n" .. line
-- reply ends with same code
until code == current and sep == " "
end
return code, reply
end
-- metatable for sock object
local metat = { __index = {} }
function metat.__index:check(ok)
local code, reply = get_reply(self.c)
if not code then return nil, reply end
if base.type(ok) ~= "function" then
if base.type(ok) == "table" then
for i, v in base.ipairs(ok) do
if string.find(code, v) then
return base.tonumber(code), reply
end
end
return nil, reply
else
if string.find(code, ok) then return base.tonumber(code), reply
else return nil, reply end
end
else return ok(base.tonumber(code), reply) end
end
function metat.__index:command(cmd, arg)
cmd = string.upper(cmd)
if arg then
return self.c:send(cmd .. " " .. arg.. "\r\n")
else
return self.c:send(cmd .. "\r\n")
end
end
function metat.__index:sink(snk, pat)
local chunk, err = c:receive(pat)
return snk(chunk, err)
end
function metat.__index:send(data)
return self.c:send(data)
end
function metat.__index:receive(pat)
return self.c:receive(pat)
end
function metat.__index:getfd()
return self.c:getfd()
end
function metat.__index:dirty()
return self.c:dirty()
end
function metat.__index:getcontrol()
return self.c
end
function metat.__index:source(source, step)
local sink = socket.sink("keep-open", self.c)
local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step)
return ret, err
end
-- closes the underlying c
function metat.__index:close()
self.c:close()
return 1
end
-- connect with server and return c object
function connect(host, port, timeout, create)
local c, e = (create or socket.tcp)()
if not c then return nil, e end
c:settimeout(timeout or TIMEOUT)
local r, e = c:connect(host, port)
if not r then
c:close()
return nil, e
end
return base.setmetatable({c = c}, metat)
end

385
src/udp.c Normal file
View File

@ -0,0 +1,385 @@
/*=========================================================================*\
* UDP object
* LuaSocket toolkit
*
* RCS ID: $Id: udp.c,v 1.30 2009/05/27 09:31:35 diego Exp $
\*=========================================================================*/
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "auxiliar.h"
#include "socket.h"
#include "inet.h"
#include "options.h"
#include "udp.h"
/* min and max macros */
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? x : y)
#endif
#ifndef MAX
#define MAX(x, y) ((x) > (y) ? x : y)
#endif
/*=========================================================================*\
* Internal function prototypes
\*=========================================================================*/
static int global_create(lua_State *L);
static int global_create6(lua_State *L);
static int meth_send(lua_State *L);
static int meth_sendto(lua_State *L);
static int meth_receive(lua_State *L);
static int meth_receivefrom(lua_State *L);
static int meth_getsockname(lua_State *L);
static int meth_getpeername(lua_State *L);
static int meth_setsockname(lua_State *L);
static int meth_setpeername(lua_State *L);
static int meth_close(lua_State *L);
static int meth_setoption(lua_State *L);
static int meth_getoption(lua_State *L);
static int meth_settimeout(lua_State *L);
static int meth_getfd(lua_State *L);
static int meth_setfd(lua_State *L);
static int meth_dirty(lua_State *L);
/* udp object methods */
static luaL_Reg udp_methods[] = {
{"__gc", meth_close},
{"__tostring", auxiliar_tostring},
{"close", meth_close},
{"dirty", meth_dirty},
{"getfd", meth_getfd},
{"getpeername", meth_getpeername},
{"getsockname", meth_getsockname},
{"receive", meth_receive},
{"receivefrom", meth_receivefrom},
{"send", meth_send},
{"sendto", meth_sendto},
{"setfd", meth_setfd},
{"setoption", meth_setoption},
{"getoption", meth_getoption},
{"setpeername", meth_setpeername},
{"setsockname", meth_setsockname},
{"settimeout", meth_settimeout},
{NULL, NULL}
};
/* socket options for setoption */
static t_opt optset[] = {
{"dontroute", opt_set_dontroute},
{"broadcast", opt_set_broadcast},
{"reuseaddr", opt_set_reuseaddr},
{"reuseport", opt_set_reuseport},
{"ip-multicast-if", opt_set_ip_multicast_if},
{"ip-multicast-ttl", opt_set_ip_multicast_ttl},
{"ip-multicast-loop", opt_set_ip_multicast_loop},
{"ip-add-membership", opt_set_ip_add_membership},
{"ip-drop-membership", opt_set_ip_drop_membersip},
{"ipv6-v6only", opt_set_ip6_v6only},
{NULL, NULL}
};
/* socket options for getoption */
static t_opt optget[] = {
{"ip-multicast-if", opt_get_ip_multicast_if},
{"ip-multicast-loop", opt_get_ip_multicast_loop},
{NULL, NULL}
};
/* functions in library namespace */
static luaL_Reg func[] = {
{"udp", global_create},
{"udp6", global_create6},
{NULL, NULL}
};
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int udp_open(lua_State *L)
{
/* create classes */
auxiliar_newclass(L, "udp{connected}", udp_methods);
auxiliar_newclass(L, "udp{unconnected}", udp_methods);
/* create class groups */
auxiliar_add2group(L, "udp{connected}", "udp{any}");
auxiliar_add2group(L, "udp{unconnected}", "udp{any}");
auxiliar_add2group(L, "udp{connected}", "select{able}");
auxiliar_add2group(L, "udp{unconnected}", "select{able}");
/* define library functions */
luaL_openlib(L, NULL, func, 0);
return 0;
}
/*=========================================================================*\
* Lua methods
\*=========================================================================*/
const char *udp_strerror(int err) {
/* a 'closed' error on an unconnected means the target address was not
* accepted by the transport layer */
if (err == IO_CLOSED) return "refused";
else return socket_strerror(err);
}
/*-------------------------------------------------------------------------*\
* Send data through connected udp socket
\*-------------------------------------------------------------------------*/
static int meth_send(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}", 1);
p_timeout tm = &udp->tm;
size_t count, sent = 0;
int err;
const char *data = luaL_checklstring(L, 2, &count);
timeout_markstart(tm);
err = socket_send(&udp->sock, data, count, &sent, tm);
if (err != IO_DONE) {
lua_pushnil(L);
lua_pushstring(L, udp_strerror(err));
return 2;
}
lua_pushnumber(L, sent);
return 1;
}
/*-------------------------------------------------------------------------*\
* Send data through unconnected udp socket
\*-------------------------------------------------------------------------*/
static int meth_sendto(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1);
size_t count, sent = 0;
const char *data = luaL_checklstring(L, 2, &count);
const char *ip = luaL_checkstring(L, 3);
unsigned short port = (unsigned short) luaL_checknumber(L, 4);
p_timeout tm = &udp->tm;
struct sockaddr_in addr;
int err;
memset(&addr, 0, sizeof(addr));
if (!inet_aton(ip, &addr.sin_addr))
luaL_argerror(L, 3, "invalid ip address");
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
timeout_markstart(tm);
err = socket_sendto(&udp->sock, data, count, &sent,
(SA *) &addr, sizeof(addr), tm);
if (err != IO_DONE) {
lua_pushnil(L);
lua_pushstring(L, udp_strerror(err));
return 2;
}
lua_pushnumber(L, sent);
return 1;
}
/*-------------------------------------------------------------------------*\
* Receives data from a UDP socket
\*-------------------------------------------------------------------------*/
static int meth_receive(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
char buffer[UDP_DATAGRAMSIZE];
size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer));
int err;
p_timeout tm = &udp->tm;
count = MIN(count, sizeof(buffer));
timeout_markstart(tm);
err = socket_recv(&udp->sock, buffer, count, &got, tm);
if (err != IO_DONE) {
lua_pushnil(L);
lua_pushstring(L, udp_strerror(err));
return 2;
}
lua_pushlstring(L, buffer, got);
return 1;
}
/*-------------------------------------------------------------------------*\
* Receives data and sender from a UDP socket
\*-------------------------------------------------------------------------*/
static int meth_receivefrom(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1);
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
char buffer[UDP_DATAGRAMSIZE];
size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer));
int err;
p_timeout tm = &udp->tm;
timeout_markstart(tm);
count = MIN(count, sizeof(buffer));
err = socket_recvfrom(&udp->sock, buffer, count, &got,
(SA *) &addr, &addr_len, tm);
if (err == IO_DONE) {
lua_pushlstring(L, buffer, got);
lua_pushstring(L, inet_ntoa(addr.sin_addr));
lua_pushnumber(L, ntohs(addr.sin_port));
return 3;
} else {
lua_pushnil(L);
lua_pushstring(L, udp_strerror(err));
return 2;
}
}
/*-------------------------------------------------------------------------*\
* Select support methods
\*-------------------------------------------------------------------------*/
static int meth_getfd(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
lua_pushnumber(L, (int) udp->sock);
return 1;
}
/* this is very dangerous, but can be handy for those that are brave enough */
static int meth_setfd(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
udp->sock = (t_socket) luaL_checknumber(L, 2);
return 0;
}
static int meth_dirty(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
(void) udp;
lua_pushboolean(L, 0);
return 1;
}
/*-------------------------------------------------------------------------*\
* Just call inet methods
\*-------------------------------------------------------------------------*/
static int meth_getpeername(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{connected}", 1);
return inet_meth_getpeername(L, &udp->sock);
}
static int meth_getsockname(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
return inet_meth_getsockname(L, &udp->sock);
}
/*-------------------------------------------------------------------------*\
* Just call option handler
\*-------------------------------------------------------------------------*/
static int meth_setoption(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
return opt_meth_setoption(L, optset, &udp->sock);
}
/*-------------------------------------------------------------------------*\
* Just call option handler
\*-------------------------------------------------------------------------*/
static int meth_getoption(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
return opt_meth_getoption(L, optget, &udp->sock);
}
/*-------------------------------------------------------------------------*\
* Just call tm methods
\*-------------------------------------------------------------------------*/
static int meth_settimeout(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
return timeout_meth_settimeout(L, &udp->tm);
}
/*-------------------------------------------------------------------------*\
* Turns a master udp object into a client object.
\*-------------------------------------------------------------------------*/
static int meth_setpeername(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
p_timeout tm = &udp->tm;
const char *address = luaL_checkstring(L, 2);
int connecting = strcmp(address, "*");
const char *port = connecting ?
luaL_checkstring(L, 3) :
luaL_optstring(L, 3, "0");
struct addrinfo connecthints;
const char *err;
memset(&connecthints, 0, sizeof(connecthints));
connecthints.ai_socktype = SOCK_DGRAM;
/* make sure we try to connect only to the same family */
connecthints.ai_family = udp->domain;
err = inet_tryconnect(&udp->sock, address, port,
tm, &connecthints);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
/* change class to connected or unconnected depending on address */
if (connecting) auxiliar_setclass(L, "udp{connected}", 1);
else auxiliar_setclass(L, "udp{unconnected}", 1);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Closes socket used by object
\*-------------------------------------------------------------------------*/
static int meth_close(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1);
socket_destroy(&udp->sock);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Turns a master object into a server object
\*-------------------------------------------------------------------------*/
static int meth_setsockname(lua_State *L) {
p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1);
const char *address = luaL_checkstring(L, 2);
const char *port = luaL_checkstring(L, 3);
const char *err;
struct addrinfo bindhints;
memset(&bindhints, 0, sizeof(bindhints));
bindhints.ai_socktype = SOCK_DGRAM;
bindhints.ai_family = udp->domain;
bindhints.ai_flags = AI_PASSIVE;
err = inet_trybind(&udp->sock, address, port, &bindhints);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
lua_pushnumber(L, 1);
return 1;
}
/*=========================================================================*\
* Library functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Creates a master udp object
\*-------------------------------------------------------------------------*/
static int udp_create(lua_State *L, int domain) {
t_socket sock;
const char *err = inet_trycreate(&sock, domain, SOCK_DGRAM);
/* try to allocate a system socket */
if (!err) {
/* allocate udp object */
p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp));
auxiliar_setclass(L, "udp{unconnected}", -1);
/* initialize remaining structure fields */
socket_setnonblocking(&sock);
if (domain == PF_INET6) {
int yes = 1;
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
(void *)&yes, sizeof(yes));
}
udp->sock = sock;
timeout_init(&udp->tm, -1, -1);
udp->domain = domain;
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
}
static int global_create(lua_State *L) {
return udp_create(L, AF_INET);
}
static int global_create6(lua_State *L) {
return udp_create(L, AF_INET6);
}

32
src/udp.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef UDP_H
#define UDP_H
/*=========================================================================*\
* UDP object
* LuaSocket toolkit
*
* The udp.h module provides LuaSocket with support for UDP protocol
* (AF_INET, SOCK_DGRAM).
*
* Two classes are defined: connected and unconnected. UDP objects are
* originally unconnected. They can be "connected" to a given address
* with a call to the setpeername function. The same function can be used to
* break the connection.
\*=========================================================================*/
#include "lua.h"
#include "timeout.h"
#include "socket.h"
/* can't be larger than wsocket.c MAXCHUNK!!! */
#define UDP_DATAGRAMSIZE 8192
typedef struct t_udp_ {
t_socket sock;
t_timeout tm;
int domain;
} t_udp;
typedef t_udp *p_udp;
int udp_open(lua_State *L);
#endif /* UDP_H */

BIN
src/udp.o Normal file

Binary file not shown.

356
src/unix.c Normal file
View File

@ -0,0 +1,356 @@
/*=========================================================================*\
* Unix domain socket
* LuaSocket toolkit
*
* RCS ID: $Id: unix.c,v 1.14 2009/05/27 09:31:35 diego Exp $
\*=========================================================================*/
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "auxiliar.h"
#include "socket.h"
#include "options.h"
#include "unix.h"
#include <sys/un.h>
/*=========================================================================*\
* Internal function prototypes
\*=========================================================================*/
static int global_create(lua_State *L);
static int meth_connect(lua_State *L);
static int meth_listen(lua_State *L);
static int meth_bind(lua_State *L);
static int meth_send(lua_State *L);
static int meth_shutdown(lua_State *L);
static int meth_receive(lua_State *L);
static int meth_accept(lua_State *L);
static int meth_close(lua_State *L);
static int meth_setoption(lua_State *L);
static int meth_settimeout(lua_State *L);
static int meth_getfd(lua_State *L);
static int meth_setfd(lua_State *L);
static int meth_dirty(lua_State *L);
static int meth_getstats(lua_State *L);
static int meth_setstats(lua_State *L);
static const char *unix_tryconnect(p_unix un, const char *path);
static const char *unix_trybind(p_unix un, const char *path);
/* unix object methods */
static luaL_Reg un[] = {
{"__gc", meth_close},
{"__tostring", auxiliar_tostring},
{"accept", meth_accept},
{"bind", meth_bind},
{"close", meth_close},
{"connect", meth_connect},
{"dirty", meth_dirty},
{"getfd", meth_getfd},
{"getstats", meth_getstats},
{"setstats", meth_setstats},
{"listen", meth_listen},
{"receive", meth_receive},
{"send", meth_send},
{"setfd", meth_setfd},
{"setoption", meth_setoption},
{"setpeername", meth_connect},
{"setsockname", meth_bind},
{"settimeout", meth_settimeout},
{"shutdown", meth_shutdown},
{NULL, NULL}
};
/* socket option handlers */
static t_opt optset[] = {
{"keepalive", opt_set_keepalive},
{"reuseaddr", opt_set_reuseaddr},
{"linger", opt_set_linger},
{NULL, NULL}
};
/* our socket creation function */
static luaL_Reg func[] = {
{"unix", global_create},
{NULL, NULL}
};
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int luaopen_socket_unix(lua_State *L) {
/* create classes */
auxiliar_newclass(L, "unix{master}", un);
auxiliar_newclass(L, "unix{client}", un);
auxiliar_newclass(L, "unix{server}", un);
/* create class groups */
auxiliar_add2group(L, "unix{master}", "unix{any}");
auxiliar_add2group(L, "unix{client}", "unix{any}");
auxiliar_add2group(L, "unix{server}", "unix{any}");
/* make sure the function ends up in the package table */
luaL_openlib(L, "socket", func, 0);
/* return the function instead of the 'socket' table */
lua_pushstring(L, "unix");
lua_gettable(L, -2);
return 1;
}
/*=========================================================================*\
* Lua methods
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Just call buffered IO methods
\*-------------------------------------------------------------------------*/
static int meth_send(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1);
return buffer_meth_send(L, &un->buf);
}
static int meth_receive(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1);
return buffer_meth_receive(L, &un->buf);
}
static int meth_getstats(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1);
return buffer_meth_getstats(L, &un->buf);
}
static int meth_setstats(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1);
return buffer_meth_setstats(L, &un->buf);
}
/*-------------------------------------------------------------------------*\
* Just call option handler
\*-------------------------------------------------------------------------*/
static int meth_setoption(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1);
return opt_meth_setoption(L, optset, &un->sock);
}
/*-------------------------------------------------------------------------*\
* Select support methods
\*-------------------------------------------------------------------------*/
static int meth_getfd(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1);
lua_pushnumber(L, (int) un->sock);
return 1;
}
/* this is very dangerous, but can be handy for those that are brave enough */
static int meth_setfd(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1);
un->sock = (t_socket) luaL_checknumber(L, 2);
return 0;
}
static int meth_dirty(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1);
lua_pushboolean(L, !buffer_isempty(&un->buf));
return 1;
}
/*-------------------------------------------------------------------------*\
* Waits for and returns a client object attempting connection to the
* server object
\*-------------------------------------------------------------------------*/
static int meth_accept(lua_State *L) {
p_unix server = (p_unix) auxiliar_checkclass(L, "unix{server}", 1);
p_timeout tm = timeout_markstart(&server->tm);
t_socket sock;
int err = socket_accept(&server->sock, &sock, NULL, NULL, tm);
/* if successful, push client socket */
if (err == IO_DONE) {
p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix));
auxiliar_setclass(L, "unix{client}", -1);
/* initialize structure fields */
socket_setnonblocking(&sock);
clnt->sock = sock;
io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv,
(p_error) socket_ioerror, &clnt->sock);
timeout_init(&clnt->tm, -1, -1);
buffer_init(&clnt->buf, &clnt->io, &clnt->tm);
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(err));
return 2;
}
}
/*-------------------------------------------------------------------------*\
* Binds an object to an address
\*-------------------------------------------------------------------------*/
static const char *unix_trybind(p_unix un, const char *path) {
struct sockaddr_un local;
size_t len = strlen(path);
int err;
if (len >= sizeof(local.sun_path)) return "path too long";
memset(&local, 0, sizeof(local));
strcpy(local.sun_path, path);
local.sun_family = AF_UNIX;
#ifdef UNIX_HAS_SUN_LEN
local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len)
+ len + 1;
err = socket_bind(&un->sock, (SA *) &local, local.sun_len);
#else
err = socket_bind(&un->sock, (SA *) &local,
sizeof(local.sun_family) + len);
#endif
if (err != IO_DONE) socket_destroy(&un->sock);
return socket_strerror(err);
}
static int meth_bind(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkclass(L, "unix{master}", 1);
const char *path = luaL_checkstring(L, 2);
const char *err = unix_trybind(un, path);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Turns a master unix object into a client object.
\*-------------------------------------------------------------------------*/
static const char *unix_tryconnect(p_unix un, const char *path)
{
struct sockaddr_un remote;
int err;
size_t len = strlen(path);
if (len >= sizeof(remote.sun_path)) return "path too long";
memset(&remote, 0, sizeof(remote));
strcpy(remote.sun_path, path);
remote.sun_family = AF_UNIX;
timeout_markstart(&un->tm);
#ifdef UNIX_HAS_SUN_LEN
remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len)
+ len + 1;
err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm);
#else
err = socket_connect(&un->sock, (SA *) &remote,
sizeof(remote.sun_family) + len, &un->tm);
#endif
if (err != IO_DONE) socket_destroy(&un->sock);
return socket_strerror(err);
}
static int meth_connect(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkclass(L, "unix{master}", 1);
const char *path = luaL_checkstring(L, 2);
const char *err = unix_tryconnect(un, path);
if (err) {
lua_pushnil(L);
lua_pushstring(L, err);
return 2;
}
/* turn master object into a client object */
auxiliar_setclass(L, "unix{client}", 1);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Closes socket used by object
\*-------------------------------------------------------------------------*/
static int meth_close(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1);
socket_destroy(&un->sock);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Puts the sockt in listen mode
\*-------------------------------------------------------------------------*/
static int meth_listen(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkclass(L, "unix{master}", 1);
int backlog = (int) luaL_optnumber(L, 2, 32);
int err = socket_listen(&un->sock, backlog);
if (err != IO_DONE) {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(err));
return 2;
}
/* turn master object into a server object */
auxiliar_setclass(L, "unix{server}", 1);
lua_pushnumber(L, 1);
return 1;
}
/*-------------------------------------------------------------------------*\
* Shuts the connection down partially
\*-------------------------------------------------------------------------*/
static int meth_shutdown(lua_State *L)
{
p_unix un = (p_unix) auxiliar_checkclass(L, "unix{client}", 1);
const char *how = luaL_optstring(L, 2, "both");
switch (how[0]) {
case 'b':
if (strcmp(how, "both")) goto error;
socket_shutdown(&un->sock, 2);
break;
case 's':
if (strcmp(how, "send")) goto error;
socket_shutdown(&un->sock, 1);
break;
case 'r':
if (strcmp(how, "receive")) goto error;
socket_shutdown(&un->sock, 0);
break;
}
lua_pushnumber(L, 1);
return 1;
error:
luaL_argerror(L, 2, "invalid shutdown method");
return 0;
}
/*-------------------------------------------------------------------------*\
* Just call tm methods
\*-------------------------------------------------------------------------*/
static int meth_settimeout(lua_State *L) {
p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1);
return timeout_meth_settimeout(L, &un->tm);
}
/*=========================================================================*\
* Library functions
\*=========================================================================*/
/*-------------------------------------------------------------------------*\
* Creates a master unix object
\*-------------------------------------------------------------------------*/
static int global_create(lua_State *L) {
t_socket sock;
int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0);
/* try to allocate a system socket */
if (err == IO_DONE) {
/* allocate unix object */
p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix));
/* set its type as master object */
auxiliar_setclass(L, "unix{master}", -1);
/* initialize remaining structure fields */
socket_setnonblocking(&sock);
un->sock = sock;
io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv,
(p_error) socket_ioerror, &un->sock);
timeout_init(&un->tm, -1, -1);
buffer_init(&un->buf, &un->io, &un->tm);
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, socket_strerror(err));
return 2;
}
}

26
src/unix.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef UNIX_H
#define UNIX_H
/*=========================================================================*\
* Unix domain object
* LuaSocket toolkit
*
* This module is just an example of how to extend LuaSocket with a new
* domain.
\*=========================================================================*/
#include "lua.h"
#include "buffer.h"
#include "timeout.h"
#include "socket.h"
typedef struct t_unix_ {
t_socket sock;
t_io io;
t_buffer buf;
t_timeout tm;
} t_unix;
typedef t_unix *p_unix;
int luaopen_socket_unix(lua_State *L);
#endif /* UNIX_H */

297
src/url.lua Normal file
View File

@ -0,0 +1,297 @@
-----------------------------------------------------------------------------
-- URI parsing, composition and relative URL resolution
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: url.lua,v 1.38 2006/04/03 04:45:42 diego Exp $
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Declare module
-----------------------------------------------------------------------------
local string = require("string")
local base = _G
local table = require("table")
module("socket.url")
-----------------------------------------------------------------------------
-- Module version
-----------------------------------------------------------------------------
_VERSION = "URL 1.0.1"
-----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
function escape(s)
return (string.gsub(s, "([^A-Za-z0-9_])", function(c)
return string.format("%%%02x", string.byte(c))
end))
end
-----------------------------------------------------------------------------
-- Protects a path segment, to prevent it from interfering with the
-- url parsing.
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
local function make_set(t)
local s = {}
for i,v in base.ipairs(t) do
s[t[i]] = 1
end
return s
end
-- these are allowed withing a path segment, along with alphanum
-- other characters must be escaped
local segment_set = make_set {
"-", "_", ".", "!", "~", "*", "'", "(",
")", ":", "@", "&", "=", "+", "$", ",",
}
local function protect_segment(s)
return string.gsub(s, "([^A-Za-z0-9_])", function (c)
if segment_set[c] then return c
else return string.format("%%%02x", string.byte(c)) end
end)
end
-----------------------------------------------------------------------------
-- Encodes a string into its escaped hexadecimal representation
-- Input
-- s: binary string to be encoded
-- Returns
-- escaped representation of string binary
-----------------------------------------------------------------------------
function unescape(s)
return (string.gsub(s, "%%(%x%x)", function(hex)
return string.char(base.tonumber(hex, 16))
end))
end
-----------------------------------------------------------------------------
-- Builds a path from a base path and a relative path
-- Input
-- base_path
-- relative_path
-- Returns
-- corresponding absolute path
-----------------------------------------------------------------------------
local function absolute_path(base_path, relative_path)
if string.sub(relative_path, 1, 1) == "/" then return relative_path end
local path = string.gsub(base_path, "[^/]*$", "")
path = path .. relative_path
path = string.gsub(path, "([^/]*%./)", function (s)
if s ~= "./" then return s else return "" end
end)
path = string.gsub(path, "/%.$", "/")
local reduced
while reduced ~= path do
reduced = path
path = string.gsub(reduced, "([^/]*/%.%./)", function (s)
if s ~= "../../" then return "" else return s end
end)
end
path = string.gsub(reduced, "([^/]*/%.%.)$", function (s)
if s ~= "../.." then return "" else return s end
end)
return path
end
-----------------------------------------------------------------------------
-- Parses a url and returns a table with all its parts according to RFC 2396
-- The following grammar describes the names given to the URL parts
-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
-- <authority> ::= <userinfo>@<host>:<port>
-- <userinfo> ::= <user>[:<password>]
-- <path> :: = {<segment>/}<segment>
-- Input
-- url: uniform resource locator of request
-- default: table with default values for each field
-- Returns
-- table with the following fields, where RFC naming conventions have
-- been preserved:
-- scheme, authority, userinfo, user, password, host, port,
-- path, params, query, fragment
-- Obs:
-- the leading '/' in {/<path>} is considered part of <path>
-----------------------------------------------------------------------------
function parse(url, default)
-- initialize default parameters
local parsed = {}
for i,v in base.pairs(default or parsed) do parsed[i] = v end
-- empty url is parsed to nil
if not url or url == "" then return nil, "invalid url" end
-- remove whitespace
-- url = string.gsub(url, "%s", "")
-- get fragment
url = string.gsub(url, "#(.*)$", function(f)
parsed.fragment = f
return ""
end)
-- get scheme
url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
function(s) parsed.scheme = s; return "" end)
-- get authority
url = string.gsub(url, "^//([^/]*)", function(n)
parsed.authority = n
return ""
end)
-- get query stringing
url = string.gsub(url, "%?(.*)", function(q)
parsed.query = q
return ""
end)
-- get params
url = string.gsub(url, "%;(.*)", function(p)
parsed.params = p
return ""
end)
-- path is whatever was left
if url ~= "" then parsed.path = url end
local authority = parsed.authority
if not authority then return parsed end
authority = string.gsub(authority,"^([^@]*)@",
function(u) parsed.userinfo = u; return "" end)
authority = string.gsub(authority, ":([^:]*)$",
function(p) parsed.port = p; return "" end)
if authority ~= "" then parsed.host = authority end
local userinfo = parsed.userinfo
if not userinfo then return parsed end
userinfo = string.gsub(userinfo, ":([^:]*)$",
function(p) parsed.password = p; return "" end)
parsed.user = userinfo
return parsed
end
-----------------------------------------------------------------------------
-- Rebuilds a parsed URL from its components.
-- Components are protected if any reserved or unallowed characters are found
-- Input
-- parsed: parsed URL, as returned by parse
-- Returns
-- a stringing with the corresponding URL
-----------------------------------------------------------------------------
function build(parsed)
local ppath = parse_path(parsed.path or "")
local url = build_path(ppath)
if parsed.params then url = url .. ";" .. parsed.params end
if parsed.query then url = url .. "?" .. parsed.query end
local authority = parsed.authority
if parsed.host then
authority = parsed.host
if parsed.port then authority = authority .. ":" .. parsed.port end
local userinfo = parsed.userinfo
if parsed.user then
userinfo = parsed.user
if parsed.password then
userinfo = userinfo .. ":" .. parsed.password
end
end
if userinfo then authority = userinfo .. "@" .. authority end
end
if authority then url = "//" .. authority .. url end
if parsed.scheme then url = parsed.scheme .. ":" .. url end
if parsed.fragment then url = url .. "#" .. parsed.fragment end
-- url = string.gsub(url, "%s", "")
return url
end
-----------------------------------------------------------------------------
-- Builds a absolute URL from a base and a relative URL according to RFC 2396
-- Input
-- base_url
-- relative_url
-- Returns
-- corresponding absolute url
-----------------------------------------------------------------------------
function absolute(base_url, relative_url)
if base.type(base_url) == "table" then
base_parsed = base_url
base_url = build(base_parsed)
else
base_parsed = parse(base_url)
end
local relative_parsed = parse(relative_url)
if not base_parsed then return relative_url
elseif not relative_parsed then return base_url
elseif relative_parsed.scheme then return relative_url
else
relative_parsed.scheme = base_parsed.scheme
if not relative_parsed.authority then
relative_parsed.authority = base_parsed.authority
if not relative_parsed.path then
relative_parsed.path = base_parsed.path
if not relative_parsed.params then
relative_parsed.params = base_parsed.params
if not relative_parsed.query then
relative_parsed.query = base_parsed.query
end
end
else
relative_parsed.path = absolute_path(base_parsed.path or "",
relative_parsed.path)
end
end
return build(relative_parsed)
end
end
-----------------------------------------------------------------------------
-- Breaks a path into its segments, unescaping the segments
-- Input
-- path
-- Returns
-- segment: a table with one entry per segment
-----------------------------------------------------------------------------
function parse_path(path)
local parsed = {}
path = path or ""
--path = string.gsub(path, "%s", "")
string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end)
for i = 1, table.getn(parsed) do
parsed[i] = unescape(parsed[i])
end
if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end
if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end
return parsed
end
-----------------------------------------------------------------------------
-- Builds a path component from its segments, escaping protected characters.
-- Input
-- parsed: path segments
-- unsafe: if true, segments are not protected before path is built
-- Returns
-- path: corresponding path stringing
-----------------------------------------------------------------------------
function build_path(parsed, unsafe)
local path = ""
local n = table.getn(parsed)
if unsafe then
for i = 1, n-1 do
path = path .. parsed[i]
path = path .. "/"
end
if n > 0 then
path = path .. parsed[n]
if parsed.is_directory then path = path .. "/" end
end
else
for i = 1, n-1 do
path = path .. protect_segment(parsed[i])
path = path .. "/"
end
if n > 0 then
path = path .. protect_segment(parsed[n])
if parsed.is_directory then path = path .. "/" end
end
end
if parsed.is_absolute then path = "/" .. path end
return path
end

395
src/usocket.c Normal file
View File

@ -0,0 +1,395 @@
/*=========================================================================*\
* Socket compatibilization module for Unix
* LuaSocket toolkit
*
* The code is now interrupt-safe.
* The penalty of calling select to avoid busy-wait is only paid when
* the I/O call fail in the first place.
*
* RCS ID: $Id: usocket.c,v 1.38 2007/10/13 23:55:20 diego Exp $
\*=========================================================================*/
#include <string.h>
#include <signal.h>
#include "socket.h"
/*-------------------------------------------------------------------------*\
* Wait for readable/writable/connected socket with timeout
\*-------------------------------------------------------------------------*/
#ifdef SOCKET_POLL
#include <sys/poll.h>
#define WAITFD_R POLLIN
#define WAITFD_W POLLOUT
#define WAITFD_C (POLLIN|POLLOUT)
int socket_waitfd(p_socket ps, int sw, p_timeout tm) {
int ret;
struct pollfd pfd;
pfd.fd = *ps;
pfd.events = sw;
pfd.revents = 0;
if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */
do {
int t = (int)(timeout_getretry(tm)*1e3);
ret = poll(&pfd, 1, t >= 0? t: -1);
} while (ret == -1 && errno == EINTR);
if (ret == -1) return errno;
if (ret == 0) return IO_TIMEOUT;
if (sw == WAITFD_C && (pfd.revents & (POLLIN|POLLERR))) return IO_CLOSED;
return IO_DONE;
}
#else
#define WAITFD_R 1
#define WAITFD_W 2
#define WAITFD_C (WAITFD_R|WAITFD_W)
int socket_waitfd(p_socket ps, int sw, p_timeout tm) {
int ret;
fd_set rfds, wfds, *rp, *wp;
struct timeval tv, *tp;
double t;
if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */
do {
/* must set bits within loop, because select may have modifed them */
rp = wp = NULL;
if (sw & WAITFD_R) { FD_ZERO(&rfds); FD_SET(*ps, &rfds); rp = &rfds; }
if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; }
t = timeout_getretry(tm);
tp = NULL;
if (t >= 0.0) {
tv.tv_sec = (int)t;
tv.tv_usec = (int)((t-tv.tv_sec)*1.0e6);
tp = &tv;
}
ret = select(*ps+1, rp, wp, NULL, tp);
} while (ret == -1 && errno == EINTR);
if (ret == -1) return errno;
if (ret == 0) return IO_TIMEOUT;
if (sw == WAITFD_C && FD_ISSET(*ps, &rfds)) return IO_CLOSED;
return IO_DONE;
}
#endif
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int socket_open(void) {
/* instals a handler to ignore sigpipe or it will crash us */
signal(SIGPIPE, SIG_IGN);
return 1;
}
/*-------------------------------------------------------------------------*\
* Close module
\*-------------------------------------------------------------------------*/
int socket_close(void) {
return 1;
}
/*-------------------------------------------------------------------------*\
* Close and inutilize socket
\*-------------------------------------------------------------------------*/
void socket_destroy(p_socket ps) {
if (*ps != SOCKET_INVALID) {
socket_setblocking(ps);
close(*ps);
*ps = SOCKET_INVALID;
}
}
/*-------------------------------------------------------------------------*\
* Select with timeout control
\*-------------------------------------------------------------------------*/
int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds,
p_timeout tm) {
int ret;
do {
struct timeval tv;
double t = timeout_getretry(tm);
tv.tv_sec = (int) t;
tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6);
/* timeout = 0 means no wait */
ret = select(n, rfds, wfds, efds, t >= 0.0 ? &tv: NULL);
} while (ret < 0 && errno == EINTR);
return ret;
}
/*-------------------------------------------------------------------------*\
* Creates and sets up a socket
\*-------------------------------------------------------------------------*/
int socket_create(p_socket ps, int domain, int type, int protocol) {
*ps = socket(domain, type, protocol);
if (*ps != SOCKET_INVALID) return IO_DONE;
else return errno;
}
/*-------------------------------------------------------------------------*\
* Binds or returns error message
\*-------------------------------------------------------------------------*/
int socket_bind(p_socket ps, SA *addr, socklen_t len) {
int err = IO_DONE;
socket_setblocking(ps);
if (bind(*ps, addr, len) < 0) err = errno;
socket_setnonblocking(ps);
return err;
}
/*-------------------------------------------------------------------------*\
*
\*-------------------------------------------------------------------------*/
int socket_listen(p_socket ps, int backlog) {
int err = IO_DONE;
socket_setblocking(ps);
if (listen(*ps, backlog)) err = errno;
socket_setnonblocking(ps);
return err;
}
/*-------------------------------------------------------------------------*\
*
\*-------------------------------------------------------------------------*/
void socket_shutdown(p_socket ps, int how) {
socket_setblocking(ps);
shutdown(*ps, how);
socket_setnonblocking(ps);
}
/*-------------------------------------------------------------------------*\
* Connects or returns error message
\*-------------------------------------------------------------------------*/
int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) {
int err;
/* avoid calling on closed sockets */
if (*ps == SOCKET_INVALID) return IO_CLOSED;
/* call connect until done or failed without being interrupted */
do if (connect(*ps, addr, len) == 0) return IO_DONE;
while ((err = errno) == EINTR);
/* if connection failed immediately, return error code */
if (err != EINPROGRESS && err != EAGAIN) return err;
/* zero timeout case optimization */
if (timeout_iszero(tm)) return IO_TIMEOUT;
/* wait until we have the result of the connection attempt or timeout */
err = socket_waitfd(ps, WAITFD_C, tm);
if (err == IO_CLOSED) {
if (recv(*ps, (char *) &err, 0, 0) == 0) return IO_DONE;
else return errno;
} else return err;
}
/*-------------------------------------------------------------------------*\
* Accept with timeout
\*-------------------------------------------------------------------------*/
int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout tm) {
SA daddr;
socklen_t dlen = sizeof(daddr);
if (*ps == SOCKET_INVALID) return IO_CLOSED;
if (!addr) addr = &daddr;
if (!len) len = &dlen;
for ( ;; ) {
int err;
if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE;
err = errno;
if (err == EINTR) continue;
if (err != EAGAIN && err != ECONNABORTED) return err;
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
}
/* can't reach here */
return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Send with timeout
\*-------------------------------------------------------------------------*/
int socket_send(p_socket ps, const char *data, size_t count,
size_t *sent, p_timeout tm)
{
int err;
*sent = 0;
/* avoid making system calls on closed sockets */
if (*ps == SOCKET_INVALID) return IO_CLOSED;
/* loop until we send something or we give up on error */
for ( ;; ) {
long put = (long) send(*ps, data, count, 0);
/* if we sent anything, we are done */
if (put > 0) {
*sent = put;
return IO_DONE;
}
err = errno;
/* send can't really return 0, but EPIPE means the connection was
closed */
if (put == 0 || err == EPIPE) return IO_CLOSED;
/* we call was interrupted, just try again */
if (err == EINTR) continue;
/* if failed fatal reason, report error */
if (err != EAGAIN) return err;
/* wait until we can send something or we timeout */
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
}
/* can't reach here */
return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Sendto with timeout
\*-------------------------------------------------------------------------*/
int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
SA *addr, socklen_t len, p_timeout tm)
{
int err;
*sent = 0;
if (*ps == SOCKET_INVALID) return IO_CLOSED;
for ( ;; ) {
long put = (long) sendto(*ps, data, count, 0, addr, len);
if (put > 0) {
*sent = put;
return IO_DONE;
}
err = errno;
if (put == 0 || err == EPIPE) return IO_CLOSED;
if (err == EINTR) continue;
if (err != EAGAIN) return err;
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
}
return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Receive with timeout
\*-------------------------------------------------------------------------*/
int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) {
int err;
*got = 0;
if (*ps == SOCKET_INVALID) return IO_CLOSED;
for ( ;; ) {
long taken = (long) recv(*ps, data, count, 0);
if (taken > 0) {
*got = taken;
return IO_DONE;
}
err = errno;
if (taken == 0) return IO_CLOSED;
if (err == EINTR) continue;
if (err != EAGAIN) return err;
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
}
return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Recvfrom with timeout
\*-------------------------------------------------------------------------*/
int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
SA *addr, socklen_t *len, p_timeout tm) {
int err;
*got = 0;
if (*ps == SOCKET_INVALID) return IO_CLOSED;
for ( ;; ) {
long taken = (long) recvfrom(*ps, data, count, 0, addr, len);
if (taken > 0) {
*got = taken;
return IO_DONE;
}
err = errno;
if (taken == 0) return IO_CLOSED;
if (err == EINTR) continue;
if (err != EAGAIN) return err;
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
}
return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Put socket into blocking mode
\*-------------------------------------------------------------------------*/
void socket_setblocking(p_socket ps) {
int flags = fcntl(*ps, F_GETFL, 0);
flags &= (~(O_NONBLOCK));
fcntl(*ps, F_SETFL, flags);
}
/*-------------------------------------------------------------------------*\
* Put socket into non-blocking mode
\*-------------------------------------------------------------------------*/
void socket_setnonblocking(p_socket ps) {
int flags = fcntl(*ps, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(*ps, F_SETFL, flags);
}
/*-------------------------------------------------------------------------*\
* DNS helpers
\*-------------------------------------------------------------------------*/
int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) {
*hp = gethostbyaddr(addr, len, AF_INET);
if (*hp) return IO_DONE;
else if (h_errno) return h_errno;
else if (errno) return errno;
else return IO_UNKNOWN;
}
int socket_gethostbyname(const char *addr, struct hostent **hp) {
*hp = gethostbyname(addr);
if (*hp) return IO_DONE;
else if (h_errno) return h_errno;
else if (errno) return errno;
else return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Error translation functions
* Make sure important error messages are standard
\*-------------------------------------------------------------------------*/
const char *socket_hoststrerror(int err) {
if (err <= 0) return io_strerror(err);
switch (err) {
case HOST_NOT_FOUND: return "host not found";
default: return hstrerror(err);
}
}
const char *socket_strerror(int err) {
if (err <= 0) return io_strerror(err);
switch (err) {
case EADDRINUSE: return "address already in use";
case EISCONN: return "already connected";
case EACCES: return "permission denied";
case ECONNREFUSED: return "connection refused";
case ECONNABORTED: return "closed";
case ECONNRESET: return "closed";
case ETIMEDOUT: return "timeout";
default: return strerror(errno);
}
}
const char *socket_ioerror(p_socket ps, int err) {
(void) ps;
return socket_strerror(err);
}
const char *socket_gaistrerror(int err) {
if (err == 0) return NULL;
switch (err) {
case EAI_AGAIN: return "temporary failure in name resolution";
case EAI_BADFLAGS: return "invalid value for ai_flags";
#ifdef EAI_BADHINTS
case EAI_BADHINTS: return "invalid value for hints";
#endif
case EAI_FAIL: return "non-recoverable failure in name resolution";
case EAI_FAMILY: return "ai_family not supported";
case EAI_MEMORY: return "memory allocation failure";
case EAI_NONAME:
return "hostname or servname not provided, or not known";
case EAI_OVERFLOW: return "argument buffer overflow";
#ifdef EAI_PROTOCOL
case EAI_PROTOCOL: return "resolved protocol is unknown";
#endif
case EAI_SERVICE: return "servname not supported for socktype";
case EAI_SOCKTYPE: return "ai_socktype not supported";
case EAI_SYSTEM: return strerror(errno);
default: return "unknown error";
}
}

42
src/usocket.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef USOCKET_H
#define USOCKET_H
/*=========================================================================*\
* Socket compatibilization module for Unix
* LuaSocket toolkit
\*=========================================================================*/
/*=========================================================================*\
* BSD include files
\*=========================================================================*/
/* error codes */
#include <errno.h>
/* close function */
#include <unistd.h>
/* fnctnl function and associated constants */
#include <fcntl.h>
/* struct sockaddr */
#include <sys/types.h>
/* socket function */
#include <sys/socket.h>
/* struct timeval */
#include <sys/time.h>
/* gethostbyname and gethostbyaddr functions */
#include <netdb.h>
/* sigpipe handling */
#include <signal.h>
/* IP stuff*/
#include <netinet/in.h>
#include <arpa/inet.h>
/* TCP options (nagle algorithm disable) */
#include <netinet/tcp.h>
#ifndef SO_REUSEPORT
#define SO_REUSEPORT SO_REUSEADDR
#endif
typedef int t_socket;
typedef t_socket *p_socket;
#define SOCKET_INVALID (-1)
#endif /* USOCKET_H */

BIN
src/usocket.o Normal file

Binary file not shown.

401
src/wsocket.c Normal file
View File

@ -0,0 +1,401 @@
/*=========================================================================*\
* Socket compatibilization module for Win32
* LuaSocket toolkit
*
* The penalty of calling select to avoid busy-wait is only paid when
* the I/O call fail in the first place.
*
* RCS ID: $Id: wsocket.c,v 1.36 2007/06/11 23:44:54 diego Exp $
\*=========================================================================*/
#include <string.h>
#include "socket.h"
/* WinSock doesn't have a strerror... */
static const char *wstrerror(int err);
/*-------------------------------------------------------------------------*\
* Initializes module
\*-------------------------------------------------------------------------*/
int socket_open(void) {
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 0);
int err = WSAStartup(wVersionRequested, &wsaData );
if (err != 0) return 0;
if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) &&
(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) {
WSACleanup();
return 0;
}
return 1;
}
/*-------------------------------------------------------------------------*\
* Close module
\*-------------------------------------------------------------------------*/
int socket_close(void) {
WSACleanup();
return 1;
}
/*-------------------------------------------------------------------------*\
* Wait for readable/writable/connected socket with timeout
\*-------------------------------------------------------------------------*/
#define WAITFD_R 1
#define WAITFD_W 2
#define WAITFD_E 4
#define WAITFD_C (WAITFD_E|WAITFD_W)
int socket_waitfd(p_socket ps, int sw, p_timeout tm) {
int ret;
fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL;
struct timeval tv, *tp = NULL;
double t;
if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */
if (sw & WAITFD_R) {
FD_ZERO(&rfds);
FD_SET(*ps, &rfds);
rp = &rfds;
}
if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; }
if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; }
if ((t = timeout_get(tm)) >= 0.0) {
tv.tv_sec = (int) t;
tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6);
tp = &tv;
}
ret = select(0, rp, wp, ep, tp);
if (ret == -1) return WSAGetLastError();
if (ret == 0) return IO_TIMEOUT;
if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED;
return IO_DONE;
}
/*-------------------------------------------------------------------------*\
* Select with int timeout in ms
\*-------------------------------------------------------------------------*/
int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds,
p_timeout tm) {
struct timeval tv;
double t = timeout_get(tm);
tv.tv_sec = (int) t;
tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6);
if (n <= 0) {
Sleep((DWORD) (1000*t));
return 0;
} else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL);
}
/*-------------------------------------------------------------------------*\
* Close and inutilize socket
\*-------------------------------------------------------------------------*/
void socket_destroy(p_socket ps) {
if (*ps != SOCKET_INVALID) {
socket_setblocking(ps); /* close can take a long time on WIN32 */
closesocket(*ps);
*ps = SOCKET_INVALID;
}
}
/*-------------------------------------------------------------------------*\
*
\*-------------------------------------------------------------------------*/
void socket_shutdown(p_socket ps, int how) {
socket_setblocking(ps);
shutdown(*ps, how);
socket_setnonblocking(ps);
}
/*-------------------------------------------------------------------------*\
* Creates and sets up a socket
\*-------------------------------------------------------------------------*/
int socket_create(p_socket ps, int domain, int type, int protocol) {
*ps = socket(domain, type, protocol);
if (*ps != SOCKET_INVALID) return IO_DONE;
else return WSAGetLastError();
}
/*-------------------------------------------------------------------------*\
* Connects or returns error message
\*-------------------------------------------------------------------------*/
int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) {
int err;
/* don't call on closed socket */
if (*ps == SOCKET_INVALID) return IO_CLOSED;
/* ask system to connect */
if (connect(*ps, addr, len) == 0) return IO_DONE;
/* make sure the system is trying to connect */
err = WSAGetLastError();
if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err;
/* zero timeout case optimization */
if (timeout_iszero(tm)) return IO_TIMEOUT;
/* we wait until something happens */
err = socket_waitfd(ps, WAITFD_C, tm);
if (err == IO_CLOSED) {
int len = sizeof(err);
/* give windows time to set the error (yes, disgusting) */
Sleep(10);
/* find out why we failed */
getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len);
/* we KNOW there was an error. if 'why' is 0, we will return
* "unknown error", but it's not really our fault */
return err > 0? err: IO_UNKNOWN;
} else return err;
}
/*-------------------------------------------------------------------------*\
* Binds or returns error message
\*-------------------------------------------------------------------------*/
int socket_bind(p_socket ps, SA *addr, socklen_t len) {
int err = IO_DONE;
socket_setblocking(ps);
if (bind(*ps, addr, len) < 0) err = WSAGetLastError();
socket_setnonblocking(ps);
return err;
}
/*-------------------------------------------------------------------------*\
*
\*-------------------------------------------------------------------------*/
int socket_listen(p_socket ps, int backlog) {
int err = IO_DONE;
socket_setblocking(ps);
if (listen(*ps, backlog) < 0) err = WSAGetLastError();
socket_setnonblocking(ps);
return err;
}
/*-------------------------------------------------------------------------*\
* Accept with timeout
\*-------------------------------------------------------------------------*/
int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len,
p_timeout tm) {
SA daddr;
socklen_t dlen = sizeof(daddr);
if (*ps == SOCKET_INVALID) return IO_CLOSED;
if (!addr) addr = &daddr;
if (!len) len = &dlen;
for ( ;; ) {
int err;
/* try to get client socket */
if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE;
/* find out why we failed */
err = WSAGetLastError();
/* if we failed because there was no connectoin, keep trying */
if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err;
/* call select to avoid busy wait */
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
}
/* can't reach here */
return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Send with timeout
* On windows, if you try to send 10MB, the OS will buffer EVERYTHING
* this can take an awful lot of time and we will end up blocked.
* Therefore, whoever calls this function should not pass a huge buffer.
\*-------------------------------------------------------------------------*/
int socket_send(p_socket ps, const char *data, size_t count,
size_t *sent, p_timeout tm)
{
int err;
*sent = 0;
/* avoid making system calls on closed sockets */
if (*ps == SOCKET_INVALID) return IO_CLOSED;
/* loop until we send something or we give up on error */
for ( ;; ) {
/* try to send something */
int put = send(*ps, data, (int) count, 0);
/* if we sent something, we are done */
if (put > 0) {
*sent = put;
return IO_DONE;
}
/* deal with failure */
err = WSAGetLastError();
/* we can only proceed if there was no serious error */
if (err != WSAEWOULDBLOCK) return err;
/* avoid busy wait */
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
}
/* can't reach here */
return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Sendto with timeout
\*-------------------------------------------------------------------------*/
int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,
SA *addr, socklen_t len, p_timeout tm)
{
int err;
*sent = 0;
if (*ps == SOCKET_INVALID) return IO_CLOSED;
for ( ;; ) {
int put = sendto(*ps, data, (int) count, 0, addr, len);
if (put > 0) {
*sent = put;
return IO_DONE;
}
err = WSAGetLastError();
if (err != WSAEWOULDBLOCK) return err;
if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;
}
return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Receive with timeout
\*-------------------------------------------------------------------------*/
int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) {
int err;
*got = 0;
if (*ps == SOCKET_INVALID) return IO_CLOSED;
for ( ;; ) {
int taken = recv(*ps, data, (int) count, 0);
if (taken > 0) {
*got = taken;
return IO_DONE;
}
if (taken == 0) return IO_CLOSED;
err = WSAGetLastError();
if (err != WSAEWOULDBLOCK) return err;
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
}
return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Recvfrom with timeout
\*-------------------------------------------------------------------------*/
int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,
SA *addr, socklen_t *len, p_timeout tm) {
int err;
*got = 0;
if (*ps == SOCKET_INVALID) return IO_CLOSED;
for ( ;; ) {
int taken = recvfrom(*ps, data, (int) count, 0, addr, len);
if (taken > 0) {
*got = taken;
return IO_DONE;
}
if (taken == 0) return IO_CLOSED;
err = WSAGetLastError();
if (err != WSAEWOULDBLOCK) return err;
if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;
}
return IO_UNKNOWN;
}
/*-------------------------------------------------------------------------*\
* Put socket into blocking mode
\*-------------------------------------------------------------------------*/
void socket_setblocking(p_socket ps) {
u_long argp = 0;
ioctlsocket(*ps, FIONBIO, &argp);
}
/*-------------------------------------------------------------------------*\
* Put socket into non-blocking mode
\*-------------------------------------------------------------------------*/
void socket_setnonblocking(p_socket ps) {
u_long argp = 1;
ioctlsocket(*ps, FIONBIO, &argp);
}
/*-------------------------------------------------------------------------*\
* DNS helpers
\*-------------------------------------------------------------------------*/
int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) {
*hp = gethostbyaddr(addr, len, AF_INET);
if (*hp) return IO_DONE;
else return WSAGetLastError();
}
int socket_gethostbyname(const char *addr, struct hostent **hp) {
*hp = gethostbyname(addr);
if (*hp) return IO_DONE;
else return WSAGetLastError();
}
/*-------------------------------------------------------------------------*\
* Error translation functions
\*-------------------------------------------------------------------------*/
const char *socket_hoststrerror(int err) {
if (err <= 0) return io_strerror(err);
switch (err) {
case WSAHOST_NOT_FOUND: return "host not found";
default: return wstrerror(err);
}
}
const char *socket_strerror(int err) {
if (err <= 0) return io_strerror(err);
switch (err) {
case WSAEADDRINUSE: return "address already in use";
case WSAECONNREFUSED: return "connection refused";
case WSAEISCONN: return "already connected";
case WSAEACCES: return "permission denied";
case WSAECONNABORTED: return "closed";
case WSAECONNRESET: return "closed";
case WSAETIMEDOUT: return "timeout";
default: return wstrerror(err);
}
}
const char *socket_ioerror(p_socket ps, int err) {
(void) ps;
return socket_strerror(err);
}
static const char *wstrerror(int err) {
switch (err) {
case WSAEINTR: return "Interrupted function call";
case WSAEACCES: return "Permission denied";
case WSAEFAULT: return "Bad address";
case WSAEINVAL: return "Invalid argument";
case WSAEMFILE: return "Too many open files";
case WSAEWOULDBLOCK: return "Resource temporarily unavailable";
case WSAEINPROGRESS: return "Operation now in progress";
case WSAEALREADY: return "Operation already in progress";
case WSAENOTSOCK: return "Socket operation on nonsocket";
case WSAEDESTADDRREQ: return "Destination address required";
case WSAEMSGSIZE: return "Message too long";
case WSAEPROTOTYPE: return "Protocol wrong type for socket";
case WSAENOPROTOOPT: return "Bad protocol option";
case WSAEPROTONOSUPPORT: return "Protocol not supported";
case WSAESOCKTNOSUPPORT: return "Socket type not supported";
case WSAEOPNOTSUPP: return "Operation not supported";
case WSAEPFNOSUPPORT: return "Protocol family not supported";
case WSAEAFNOSUPPORT:
return "Address family not supported by protocol family";
case WSAEADDRINUSE: return "Address already in use";
case WSAEADDRNOTAVAIL: return "Cannot assign requested address";
case WSAENETDOWN: return "Network is down";
case WSAENETUNREACH: return "Network is unreachable";
case WSAENETRESET: return "Network dropped connection on reset";
case WSAECONNABORTED: return "Software caused connection abort";
case WSAECONNRESET: return "Connection reset by peer";
case WSAENOBUFS: return "No buffer space available";
case WSAEISCONN: return "Socket is already connected";
case WSAENOTCONN: return "Socket is not connected";
case WSAESHUTDOWN: return "Cannot send after socket shutdown";
case WSAETIMEDOUT: return "Connection timed out";
case WSAECONNREFUSED: return "Connection refused";
case WSAEHOSTDOWN: return "Host is down";
case WSAEHOSTUNREACH: return "No route to host";
case WSAEPROCLIM: return "Too many processes";
case WSASYSNOTREADY: return "Network subsystem is unavailable";
case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range";
case WSANOTINITIALISED:
return "Successful WSAStartup not yet performed";
case WSAEDISCON: return "Graceful shutdown in progress";
case WSAHOST_NOT_FOUND: return "Host not found";
case WSATRY_AGAIN: return "Nonauthoritative host not found";
case WSANO_RECOVERY: return "Nonrecoverable name lookup error";
case WSANO_DATA: return "Valid name, no data record of requested type";
default: return "Unknown error";
}
}

Some files were not shown because too many files have changed in this diff Show More