lua-identd  Check-in [980ddd8035]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Initial Commit:

Bring lua-identd to life and allow others to use it beneficially.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:980ddd80357f59a194b0b2f8f4c8eea85ad9d2c706077611555b52fb2d09ddb7
User & Date: llmII 2018-06-22 20:44:35
Context
2018-06-22
20:46
Add license: check-in: 165a96e5ea user: llmII tags: trunk
20:44
Initial Commit: check-in: 980ddd8035 user: llmII tags: trunk
19:41
initial empty check-in check-in: 9b09fe787a user: llmII tags: trunk
Changes

Added README.md.









































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# Why lua-identd?

Oidentd is complex. Identd is a protocol best relegated. The only
reason we still use either of these things is to keep "~" from
showing up in front of our ident on IRC. Oidentd had a bug on
FreeBSD that when it was built in one's own repos, vs utilized
from FreeBSD's quarterly branch repos, it could not service IPv6
appropriately. In the end, oidentd hasn't seen updates in years,
but it may have picked up development again recently. Even so,
why run something that does all the actual work of an identd,
when you aren't and shouldn't be using it for anything other than
IRC likely. If you're worrying with IRC and are using a
bouncer (likely ZNC), then why not have a program that'll run
alongside it and handle it for you, without doing all the real
checks behind the scenes.

Enter lua-identd. It's an identd written in lua for the express
purpose of integrating with ZNC. It might support other ways as
well in the future for those who wish to have something of the
sorts with their own ways of doing things than using a ZNC.

For now, it's a small, networked process, that should
be (hopefully) bug-free in regards to it's mandate, service ident
requests from IRC servers for your ZNC. It does not daemonize
itself. It is another program's responsibility to daemonize it.
The following should give an idea of it's useage. Don't look at
it as hard, the instructions below may seem lengthy but they are
fairly in depth. About half of it could be omitted as hand
holding or FAQ's. Let us know how it works for you (or doesn't)
by submitting a bug request or commenting on our "thanks for the
good work" issue.

# Setup instructions:

**This is designed to be ran as root. We encourage you to overview
the code and insure that it will not violate your machine.**

To get this running the following things will be needed:

* [penlight](https://github.com/stevedonovan/Penlight)
* [cqueues](https://25thandclement.com/~william/projects/cqueues.html)
* [lua (5.3)](https://www.lua.org/)

Optionally, to make the process easier, use [luarocks](https://luarocks.org/) to install
the formerly specified.

If you're running FreeBSD, you may need to manually install
cqueues like the following:

```
fetch https://github.com/wahern/cqueues/archive/rel-20171014.tar.gz
tar xvf rel-20171014.tar.gz
cd cqueues-rel-20171014
make all 5.3
make install
```

The cqueues example assumes a **root** user will run `make install`,
if not you'll need to do some edits to get it to install into a
different location. It does require the `gmake` program to be
installed `pkg install gmake`.

If you need to install luarocks from source, the following
directions are specific for FreeBSD and will only need slight
tweaking if you're using a Linux Distribution:

```
# if freebsd, use fetch, otherwise use wget
fetch https://github.com/luarocks/luarocks/archive/v2.4.4.tar.gz
tar xvf v2.4.4.tar.gz
cd luarocks-2.4.4
# change with-lua-include if using custom built lua and/or not on FreeBSD
./configure --with-lua-include=/usr/local/include/lua53
make build
make install
```

Once again, this assumes the `make install` step will be ran as
**root**, it's left up to the user to do differently if they wish to
have luarocks and/or cqueues not install system-wide.

If you're running FreeBSD you'll likely want to make use of the
program `daemon` that comes with FreeBSD. If you're running a
Linux Distribution you'll probably wish to attain software such
as `daemonize` which is of sorts like daemon. It is up to you to
create either a rc.conf, SysV init, OpenRC init, or systemd unit
file. For our purposes we'll assume a moderately featureful
version of cron and that you either have daemon, or daemonize and
use these in the stead of tying into a system init.

To run, if you used luarocks, you need to get luarocks to setup
your script, you'll need to use the actual locations of where you
placed lua-identd (lidentd.lua) and so forth (we'll use
`$lidentd_path` as the var for it):

```
luarocks path > ./start-lidentd.sh
echo $lua53_binary $lidentd_path >> ./start-lidentd.sh
```

Then crontab -e and add a (either daemonize or daemon) line to cron like so:

```
@reboot daemon $path/start-lidentd.sh
```

where `$path` evaluates to exactly where you stuck `./start-lidentd.sh`.

If you want PID files and/or Lock files etc, it's up to you to
read the documentation of "daemon" (FreeBSD), daemonize (Linux),
or whatever tool you deign to use for this purpose of starting
lua-identd at boot.

To setup the lua-identd program, just open it up, and look for
the "Configuration Below" and "Configuration ends here" lines,
all between it is for user editing. `idp` is where znc will place
it's ident lines (znc identfile module). `port` is the port that
it'll run on (if you wish to run it non-root you'll need to
configure port forwarding for IPv4 and IPv6 to a different
port). `address` defaults to "::" which is basically all IPv6 and
IPv4, which you should leave be unless you're using multiple znc
processes and thus need multiple lidentd processes to listen on
different addresses (which may indicate that you are absent of
the knowledge of znc's ability to connect to multiple networks
and support multiple users). `log_config` defines what `dir`
it'll log in, and what level's it'll log, and what level it'll
log to the logfile it'll manage, or print to screen.

To setup the ZNC, you'll need to do the following:

```
/msg *identfile setformat %ident%
/msg *identfile setfile /home/znc/.oidentd.conf
```

The selection of file names and so forth are completely arbitrary
and you are welcome to change things up as the system should be
flexible enough to handle differences. The location of the file
the lidentd software depends to have the related ident servicing
text that ZNC provides can be anywhere you choose. The log
locations for the lidentd software can go anywhere you choose.
Much of this is more example than absolute the final say so on
what to do to get up and running. Do customize this to how you
wish to run your system.

Remember, lua-identd is designed to be ran as root, anything
different and you've got some nice configuration to handle on
your own.

Added lidentd.lua.

































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
--[[
  lidentd - An identd for use with ZNC in lua

  Copyright (c) 2018 LLMII <dev@amlegion.org>

  Permission to use, copy, modify, and/or distribute this software for
  any purpose with or without fee is hereby granted, provided that the
  above copyright notice and this permission notice appear in all
  copies.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
  WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
  AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
  OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  PERFORMANCE OF THIS SOFTWARE.
]]

-- For setup instructions see https://code.amlegion.org/lua-identd

-- Configuration Below
local idp       = '/home/znc/.oidentd.conf'
local port      = 113
local address   = '::'
local log_config = {
   dir = '/var/log/identd/',
   default = {
      levels = {
         write = 'info',
         print = 'info'
      }
   }
}
-- Configuration ends here

local path      = require 'pl.path'
local lfs       = require 'lfs'
local cqueues   = require 'cqueues'
local socket    = require 'cqueues.socket'
local signal    = require 'cqueues.signal'

-- classes defining logging
local log_manager
do
   local logfile
   do
      -- private functions
      local getlogstr = function(level, str, lineinfo, log, logname)
         return string.format(
            '[%s/%-6s%s] %s: %s',
            logname,
            level,
            os.date(),
            lineinfo,
            str
         )
      end

      local nop = function() end

      local log_levels = {
         trace  = 1,
         debug  = 2,
         info   = 3,
         warn   = 4,
         error  = 5,
         fatal  = 6
      }

      -- close the file associated with this log
      local logfile_base = {
         close = function(self)
            if self.file then
               self.file:close()
            end
         end,

         -- write line to log file
         write = function(self, str)
            -- make sure we write to today's log file
            if os.date('%d.%m.%Y') ~= self.date then
               local fname = self.dir .. os.date('%d.%m.%Y') ..
                  '-' .. self.log_name .. '.log'
               if self.file then
                  self.file:close()
               end
               self.file = io.open(fname, 'a')
            end

            local ok, err = pcall(
               function()
                  self.file:write(str)
                  self.file:write('\n')
                  self.file:flush()
               end
            )

            if not ok then
               print('name: ' .. self.log_name .. '--- file: ' ..
                        self.file .. '::: Failed to write. Error: ' ..
                        err)
            end
         end,

         -- write log line to stdout
         print = function(self, str)
            print(str)
         end,

         -- when finalized close file
         __gc = function(self)
            if self.file then
               self.file:close()
            end
         end
      }

      logfile_base.__index = logfile_base

      local logfile_class = setmetatable(
         {
            __init = function(self, config, name)
               self._conf = config[name] or config['default']
               self.log_name = name
               self.dir = config.dir

               -- populate the actual logging functions
               for level, _ in pairs(log_levels) do
                  self[level] = nop
                  for where, when in pairs(self._conf.levels) do
                     if log_levels[level] >= log_levels[when] then
                        local f = self[level]
                        self[level] = function(self, fmt, di)
                           if di ~= nil then
                              di = di + 1
                           else
                              di = 2
                           end

                           local info = debug.getinfo(di, 'Sl')
                           local lineinfo = info.short_src .. ':' ..
                              info.currentline
                           local nameU = level:upper()
                           self[where]
                           (
                              self,
                              getlogstr
                              (
                                 level, fmt, lineinfo, nameU, self.log_name
                              )
                           )
                           f(self, fmt, di)
                        end
                     end
                  end
               end
               return self
            end
         },
         {
            __index = logfile_base,

            __call = function(cls, ...)
               return cls.__init(setmetatable({}, logfile_base), ...)
            end
         }
      )

      logfile = logfile_class
   end

   -- manages logfiles
   local log
   do
      local log_base = {
         -- opens a log file with the log name
         open = function(self, name)
            if not self.logs[name] then
               self.logs[name] = logfile(self._conf, name)
            end

            return self.logs[name]
         end,

         -- closes a log file named name
         close = function(self, name)
            if self.logs[name] then
               self.logs[name]:close()
               logs[name] = nil
            end
         end,

         -- closes all logs
         closeall = function(self)
            for _, log in pairs(self.logs) do
               log:close()
            end
            self.logs = {}
         end,

         __gc = function(self)
            self:closeall()
         end
      }

      log_base.__index = log_base

      local log_class = setmetatable(
         {
            __init = function(self, config)
               self._conf = config
               self.logs = {}
               return self
            end
         },
         {
            __index = log_base,
            __call = function(cls, ...)
               return cls.__init(setmetatable({}, log_base), ...)
            end
         }
      )

      log = log_class
   end

   log_manager = log
end

local start
do
   local cq = cqueues.new()
   local sl = signal.listen(signal.SIGINT)
   local ending = false
   local listener, handler, read_ident_file, generate_ident
   local sighandler, process_loop, init_log
   local logger, log, clear, errcheck_client
   local err_fmt = 'Err: %s, Msg: %s, Code: %s, Thread: %s, Polled: %s, Fd: %s'

   function process_loop()
      local ok, err = pcall(
         function()
            local err, msg, ctx_code, ctx_thread, ctx_polled, ctx_fd
            while not ending do
               err, msg, ctx_code, ctx_thread, ctx_polled, ctx_fd = cq:step()

               if not err then
                  log:error(
                     err_fmt:format
                     (
                        err, msg, ctx_code,
                        ctx_thread, ctx_polled, ctx_fd
                     )
                  )
                  clear = true
               end
            end
         end
      )
      if not ok then
         log:error('Cqueues threw big error: %s', err)
      end
      process_loop()
   end

   function init_log()
      lfs.mkdir(log_config.dir)
      log = logger:open('identd')
   end

   function sighandler()
      local signo
      while true do
         signo = sl:wait()
         if signo == signal.SIGINT then
            ending = true
         end
      end
   end

   function errcheck_client(con)
      local ok, err = pcall(
         function()
            handler(con)
         end
      )
      if not ok then
         log:error(
            ('Got error while handling client: %s'):format(err)
         )
         err = con:clearerr()
         log:error(('Socket errors: %s'):format(err))
      end
   end

   function listener()
      clear = false
      local srv = socket.listen {
         v6only = false,
         host = address,
         port = port
      }

      log:info('Listening on ' .. address .. ' port ' .. port)

      for con in srv:clients() do
         if clear then
            local errors = srv:clearerr()
            log:error(('%s'):format(errors))
            clear = false
         end

         log:trace 'Got client'
         cqueues.running():wrap
         (
            function()
               errcheck_client(con)
            end
         )
      end
   end

   function read_ident_file()
      log:trace("Reading ident file: " .. idp)
      local idf = io.open(idp, 'r')
      local idu = idf:read('l') or 'user'
      log:trace("Read ident: " .. idu)

      idf:close()

      return idu
   end

   function generate_ident(con)
      log:trace 'Generating ident'
      return ('%s : USERID : UNIX-BSD : %s'):format
      (
         con:read('*l'),
         read_ident_file()
      )
   end

   function handler(con)
      local _, caddr, _ = con:peername()
      con:setmode('t', 'b')
      local id = generate_ident(con)
      con:write(id .. '\r\n')
      con:flush()
      con:close()
      log:info('Handled request from: ' .. caddr .. '. Reply=' ..id)
   end

   function start()
      logger = log_manager(log_config)
      init_log()
      log:info('Starting lidentd')
      signal.block(signal.SIGINT)
      cq:wrap(listener)
      cq:wrap(sighandler)
      process_loop()
      log:info('Ending lidentd')
      logger:closeall()
   end
end

start()