1#!%TCLSH%
2
3#
4# Search for a name or an IP/MAC address and display all sessions
5#
6# History
7#   2004/11/14 : jean     : design
8#   2005/06/04 : jean     : search optimization (gen-ipmac and gen-portmac)
9#			    remove SQL joints, 2 requests and tcl processing
10#   2010/11/30 : pda/jean : integration a Netmagis
11#   2010/12/12 : pda      : i18n
12#   2010/12/26 : pda      : use cgi-dispatch
13#
14
15#
16# Template pages used by this script
17#
18
19set conf(page)          mac.html
20set conf(pagesearch)	macsearch.html
21
22#
23# Next actions
24#
25
26set conf(next)		"mac"
27
28#
29# Script parameters
30#
31
32set conf(tabsession) {
33    global {
34        chars {12 normal}
35        align {left}
36        botbar {yes}
37        columns {15 35 15 35}
38    }
39    pattern Title {
40        vbar {yes}
41        column {
42            multicolumn {4}
43            align {center}
44            chars {14 bold}
45        }
46        vbar {yes}
47    }
48    pattern Rem {
49        vbar {yes}
50        column {
51            multicolumn {4}
52            align {center}
53            chars {14}
54        }
55        vbar {yes}
56    }
57    pattern Close {
58        vbar {yes}
59        column {
60            multicolumn {2}
61        }
62        vbar {yes}
63        column {
64            multicolumn {2}
65        }
66        vbar {yes}
67    }
68    pattern Active {
69        vbar {yes}
70        column {
71            multicolumn {2}
72            chars {bold}
73        }
74        vbar {yes}
75        column {
76            multicolumn {2}
77            chars {bold}
78        }
79        vbar {yes}
80    }
81    pattern Info {
82        vbar {yes}
83        column {
84	}
85        vbar {yes}
86        column {
87            multicolumn {3}
88            chars {bold}
89	    format {raw}
90        }
91        vbar {yes}
92    }
93}
94
95set conf(tabmenuipmac) {
96    global {
97        chars {12 normal}
98        align {left}
99        botbar {yes}
100        columns {12 48 15 25}
101    }
102    pattern Data {
103        vbar {yes}
104        column {
105	    format {raw}
106	}
107        vbar {yes}
108        column {
109	    format {raw}
110	}
111        vbar {yes}
112        column {
113	    format {raw}
114	}
115        vbar {yes}
116        column { }
117        vbar {yes}
118    }
119    pattern Title {
120        vbar {yes}
121        column {
122            chars {bold}
123	}
124        vbar {yes}
125        column {
126            chars {bold}
127	}
128        vbar {yes}
129        column {
130            chars {bold}
131        }
132        vbar {yes}
133        column {
134            chars {bold}
135        }
136        vbar {yes}
137    }
138}
139
140set conf(tabmenuportmac) {
141    global {
142        chars {12 normal}
143        align {left}
144        botbar {yes}
145        columns {5 5 10 10 25 25}
146    }
147    pattern Data {
148        vbar {yes}
149        column {
150	    format {raw}
151	}
152        vbar {yes}
153        column {
154	    format {raw}
155	}
156        vbar {yes}
157        column {
158	    format {raw}
159	}
160        vbar {yes}
161        column {
162	}
163        vbar {yes}
164        column {
165	    format {raw}
166	}
167        vbar {yes}
168        column {
169	}
170        vbar {yes}
171    }
172    pattern Title {
173        vbar {yes}
174        column {
175            chars {bold}
176	}
177        vbar {yes}
178        column {
179            chars {bold}
180	}
181        vbar {yes}
182        column {
183            chars {bold}
184	}
185        vbar {yes}
186        column {
187            chars {bold}
188        }
189        vbar {yes}
190        column {
191            chars {bold}
192        }
193        vbar {yes}
194        column {
195            chars {bold}
196        }
197        vbar {yes}
198    }
199}
200
201#
202# Script parameters
203#
204
205set conf(sqldateformat) {'DD/MM/YYYY HH24:MI:SS'}
206set conf(clockformat)   {%d/%m/%Y %H:%M:%S}
207
208#
209# Netmagis general library
210#
211
212source %LIBNETMAGIS%
213
214# ::webapp::cgidebug ; exit
215
216##############################################################################
217# Utility functions
218##############################################################################
219
220#
221# Guess parameter type
222#
223# Input:
224#   - mdbfd : mac database handle
225#   - ndbfd : netmagis database handle
226#   - param : parameter given by the user
227#   - _msg : in return, error message
228# Output:
229#   - return value: empty list (error) or list {type param} where
230#	- type = "ip", "name", "mac", "idipmac", ...
231#	- param = canonical representation of parameter
232#   - msg : empty string or error message
233#
234
235proc guess-type {mdbfd ndbfd param _msg} {
236    upvar $_msg msg
237
238    set type ""
239    set msg  ""
240
241    set ipversion [::ip::version $param]
242
243    set mac_re {[0-9a-f]{1,2}(:[0-9a-f]{1,2}){5}}
244    set ip4_re {[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+}
245    set ip6_re {[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+}
246    set port_re {[0-9a-zA-Z/.\-]+}
247    set vlan_re {[0-9]+}
248    set ipmac_assoc_re "$mac_re,$ip4_re"
249    set portmac_assoc_re "$ip4_re,$mac_re,$port_re,$vlan_re"
250    set eq_re $ip4_re
251
252
253    if {[regexp "^$mac_re\$" $param]} then {
254	# MAC address
255	set type "mac"
256    } elseif {[regexp {^[0-9a-f]{4}(.[0-9a-f]{4}){2}$} $param]} then {
257	# MAC address with Cisco format (00AB.1234.5678)
258	set n {([0-9a-f]{2})}
259	regsub "^$n$n.$n$n.$n$n" $param {\1:\2:\3:\4:\5:\6} param
260	set type "mac"
261    } elseif {[regexp {^[0-9a-f]{6}-[0-9a-f]{6}$} $param]} then {
262	# MAC address with HP format (00206b-7a1477)
263	set n {([0-9a-f]{2})}
264	regsub "^$n$n$n-$n$n$n" $param {\1:\2:\3:\4:\5:\6} param
265	set type "mac"
266    } elseif {[regexp "^portmac_assoc:($portmac_assoc_re)\$" $param dummy \
267		portmac_assoc]} then {
268        # portmac association
269	set type "portmac_assoc"
270	set param $portmac_assoc
271    } elseif {[regexp "^ipmac_assoc:($ipmac_assoc_re)\$" $param dummy \
272		ipmac_assoc]} then {
273        # ipmac association
274	set type "ipmac_assoc"
275	set param $ipmac_assoc
276    } elseif {[regexp "^eq:($eq_re)\$" $param dummy eq]} then {
277        # By equipment
278	set type "equipement"
279	set param $eq
280    } elseif {[regexp "^vlan:($vlan_re)\$" $param dummy vlan]} then {
281        # By VLAN
282	set type "vlan"
283	set param $vlan
284    } elseif {$ipversion == 4 || $ipversion == 6} then {
285	# IP Address
286        set msg [check-ip-syntax $ndbfd $param "inet"]
287        if {$msg eq ""} then {
288  	    set type "ip"
289	} else {
290	    return {}
291	}
292    } elseif {[regexp {^[a-z0-9\-.]+$} $param]} then {
293        # name
294	set type "name"
295    } else {
296	# Other
297	set msg [mc "Invalid search string '%s'" $param]
298	return {}
299    }
300
301    return [list $type $param]
302}
303
304#
305# Display an HTML result with search result
306#
307# Input:
308#   - mdbfd : mac database handle
309#   - ndbfd : netmagis database handle
310#   - type : search type (ip, mac, etc.)
311#   - param : search criterion
312# Output:
313#   - return value : HTML code
314#
315
316proc html-result {mdbfd ndbfd type param} {
317    set tab ""
318    switch $type {
319	name {
320	    set lip [resolve "name" $param resmsg]
321	    if {$resmsg ne ""} {
322		d error "resolve: $resmsg"
323	    }
324	    foreach ip $lip {
325		append tab [gen-choice-ipmac $mdbfd $ndbfd $ip ""]
326	    }
327	}
328	ip {
329	    append tab [gen-choice-ipmac $mdbfd $ndbfd $param ""]
330	}
331	portmac_assoc {
332	    append tab [gen-portmac-assoc $mdbfd $ndbfd $param]
333	}
334	ipmac_assoc {
335	    append tab [gen-ipmac-assoc $mdbfd $ndbfd $param]
336	}
337	equipement {
338	    append tab [gen-eq $mdbfd $ndbfd $param]
339	}
340	vlan {
341	    append tab [gen-vlan $mdbfd $ndbfd $param]
342	}
343	mac {
344	    append tab [gen-choice-ipmac $mdbfd $ndbfd "" $param]
345 	    append tab [gen-mac $mdbfd $ndbfd $param]
346	}
347	default {
348	    d error [mc "Internal error: invalid parameter type '%s'" $type]
349	}
350    }
351    return $tab
352}
353
354#
355# Display ip-mac association
356# Input:
357#      ipmac_assoc : string containing "ip,mac"
358# Return:
359#      html string containing ip-mac association sessions
360#
361
362proc gen-ipmac-assoc {mdbfd ndbfd ipmac_assoc} {
363    global conf
364
365    #
366    # Check if IP and MAC addresses exist
367    #
368
369    lassign [split $ipmac_assoc ","] searchmac searchip
370    set ip ""
371    set mac ""
372    set sql "SELECT (data).ip, (data).mac FROM mac.ipmac
373		WHERE (data).ip = '$searchip' AND (data).mac = '$searchmac'
374		LIMIT 1"
375    pg_select $mdbfd $sql tab {
376	set ip $tab(ip)
377	set mac $tab(mac)
378    }
379    if {$ip eq ""} then {
380	return [mc "IP-MAC association (%1$s,%2$s) not found" $searchip $searchmac]
381    }
382
383    set lines {}
384
385    set dnsname [resolve "ip" $ip resmsg]
386    if {$resmsg ne ""} {
387	d error "resolve: $resmsg"
388    }
389
390    set iplink [gen-link [display-ip $ip $dnsname] $ip]
391    lappend lines [list Info [mc "IP address"] $iplink]
392
393    lappend lines [list Info [mc "MAC address"] \
394			[gen-link [display-mac $mdbfd $mac] $mac] \
395		    ]
396
397    #
398    # Display sessions
399    #
400
401    set found 0
402    set sql "SELECT  to_char(start,$conf(sqldateformat)) AS dstart,
403			to_char(stop,$conf(sqldateformat)) AS dstop,
404			closed AS closed
405		FROM mac.ipmac
406		WHERE (data).ip = '$ip' AND (data).mac = '$mac'
407		ORDER BY start DESC"
408    pg_select $mdbfd $sql tab2 {
409	if {$tab2(closed)} then { set pat "Close" } else { set pat "Active" }
410	if {$found == 0} then {
411	    lappend lines [list Active [mc "Start"] [mc "End"]]
412	    set found 1
413	}
414	lappend lines [list $pat $tab2(dstart) $tab2(dstop)]
415    }
416
417    if {$found == 0} then {
418	lappend lines [list Rem [mc "No IP-MAC association found"]]
419    }
420
421    #
422    # Format result
423    #
424
425    return [::arrgen::output "html" $conf(tabsession) $lines]
426}
427
428#
429# Display a portmac session list, for a given idportmac
430#
431
432proc gen-portmac-assoc {mdbfd ndbfd portmac_assoc} {
433    global conf
434
435    lassign [split $portmac_assoc ","] searchsrc searchmac searchport searchvlan
436    if {$searchvlan eq ""} then {
437	set searchvlan 0
438    }
439    set lines {}
440    set sql "SELECT (data).mac AS mac,
441		    (data).port AS port,
442		    src AS src,
443		    (data).vlanid AS vlanid
444		FROM mac.portmac
445		WHERE (data).mac='$searchmac'
446			AND lower((data).port)='$searchport'
447			AND (data).vlanid=$searchvlan
448			AND src='$searchsrc'"
449    pg_select $mdbfd $sql tab {
450	lappend lines [list "Info" \
451			    [mc "MAC address"] \
452			    [gen-link [display-mac $mdbfd $tab(mac)] $tab(mac)] \
453			]
454	set eq [get-eq-name $ndbfd $tab(src)]
455	lappend lines [list "Info" \
456			    [mc "Equipment"] \
457			    [gen-link $eq "eq:$tab(src)"] \
458			]
459	lappend lines [list "Info" \
460			    [mc "Interface"] \
461			    $tab(port) \
462			]
463	set vlan [display-vlan $ndbfd $tab(vlanid)]
464	lappend lines [list "Info" \
465			    [mc "Vlan"] \
466			    [gen-link $vlan "vlan:$tab(vlanid)"] \
467			]
468    }
469
470    set found 0
471    set sql "SELECT to_char(portmac.start,$conf(sqldateformat)) AS dstart,
472		    to_char(portmac.stop,$conf(sqldateformat)) AS dstop,
473		    portmac.closed AS closed
474		FROM mac.portmac
475		WHERE (data).mac='$searchmac'
476			AND (data).port='$searchport'
477			AND (data).vlanid=$searchvlan
478		ORDER BY portmac.stop DESC"
479    pg_select $mdbfd $sql tab2 {
480	if {$found==0} then {
481	    lappend lines [list Active [mc "Start"] [mc "End"]]
482	    set found 1
483	}
484
485	if {$tab2(closed)} then { set pat "Close" } else { set pat "Active" }
486	lappend lines [list $pat $tab2(dstart) $tab2(dstop)]
487    }
488
489    return [::arrgen::output "html" $conf(tabsession) $lines]
490}
491
492#
493# Display a menu of the different IP-MAC associations given an IP or MAC address
494#
495# Input:
496#  - ip : ip address (may be null)
497#  - mac : mac address (may be null)
498#
499# Output:
500#  - return value : HTML code
501#
502
503proc gen-choice-ipmac {mdbfd ndbfd ip mac} {
504    global conf
505
506    # This case should not happen
507    if {$mac eq "" && $ip eq ""} then {
508	return ""
509    }
510
511    set table ""
512    set lines {}
513    lappend lines [list Title \
514			[mc "Sessions"] \
515			[mc "IP address"] \
516			[mc "MAC address"] \
517			[mc "Last occurrence"] \
518		    ]
519    set crit {}
520
521    if {$ip ne ""} then {
522	lappend crit " (data).ip='$ip' "
523    }
524
525    if {$mac ne ""} then {
526	lappend crit " (data).mac='$mac' "
527    }
528
529    #
530    # Search all ip-mac associations and last occurrence date
531    #
532
533    set n 0
534    set sql "SELECT DISTINCT (data).ip AS ip, (data).mac AS mac
535		FROM mac.ipmac WHERE [join $crit AND]"
536    set d {}
537    pg_select $mdbfd $sql tab {
538	incr n
539	set ipmac_assoc_link [gen-link [mc "Details"] "ipmac_assoc:$tab(mac),$tab(ip)"]
540	set dnsname [resolve "ip" $tab(ip) resmsg]
541	if {$resmsg ne ""} {
542	    d error "resolve: $resmsg"
543	}
544	set iplink [gen-link [display-ip $tab(ip) $dnsname] $tab(ip)]
545
546	pg_select $mdbfd "SELECT extract(epoch from max(stop)) AS laststop
547		FROM mac.ipmac
548		WHERE (data).ip='$tab(ip)' AND (data).mac='$tab(mac)'" tab2 {
549	    set laststop $tab2(laststop)
550	}
551
552	if {$laststop ne ""} then {
553	    set laststop [expr int($laststop)]
554	} else {
555	    set laststop 0
556	}
557	set maclink [gen-link [display-mac $mdbfd $tab(mac)] $tab(mac)]
558	lappend d [list Data $ipmac_assoc_link $iplink $maclink $laststop]
559    }
560
561    #
562    # Sort list and convert date
563    #
564
565    foreach l [lsort -index end -integer $d] {
566	set t [lindex $l end]
567	set date [clock format $t -format "$conf(clockformat)"]
568	if {[regexp {^01/01/1970} $date]} then {
569	    set date [mc "(no date)"]
570	}
571	lappend lines [lreplace $l end end $date]
572    }
573
574    if {$n > 0} then {
575	set table [mc {%1$s MAC-IP address associations found for %2$s:} $n "$mac$ip"]
576	append table "<br>"
577	append table [::arrgen::output "html" $conf(tabmenuipmac) $lines]
578    } else {
579	set table [mc "No MAC-IP address association found for %s" "$mac$ip"]
580	append table "<br>"
581    }
582
583    append table "<br>"
584    return $table
585}
586
587#
588# Display a menu of the different MAC-port-vlan associations given a
589# MAC address. This is a just a wrapper to gen-choice-portmac
590#
591# Input:
592#  - mac : MAC address
593#
594# Output:
595#  - return value : HTML code
596#
597
598proc gen-mac {mdbfd ndbfd mac} {
599    if {$mac eq ""} then {
600	return ""
601    }
602    return [gen-choice-portmac $mdbfd $ndbfd "(data).mac='$mac'"]
603}
604
605#
606# Display a menu of MAC-Port-Vlan for an equipment
607#
608# Input:
609#   - mdbfd : mac database handle
610#   - ndbfd : netmagis database handle
611#   - eq : equipment ip address
612#
613# Output:
614#  - return value : HTML code
615#
616
617proc gen-eq {mdbfd ndbfd eq} {
618    if {$eq eq ""} then {
619	return [mc "No equipment address"]
620    }
621    return [gen-choice-portmac $mdbfd $ndbfd "portmac.src='$eq'"]
622}
623
624#
625# Display a menu of MAC-Port-Vlan for a Vlan
626#
627# Input:
628#   - vlanid : vlan-id
629#
630# Output:
631#  - return value : HTML code
632#
633
634proc gen-vlan {mdbfd ndbfd vlanid} {
635    if {[display-vlan $ndbfd $vlanid] eq ""} then {
636	return [mc "Vlan '%s' not found" $vlanid]
637    }
638    return [gen-choice-portmac $mdbfd $ndbfd "(data).vlanid=$vlanid"]
639}
640
641#
642# Search all distinct Port-MAC-vlan associations and last occurrence date
643# according to a criterion (SQL expression).
644#
645
646proc gen-choice-portmac {mdbfd ndbfd crit} {
647    global conf
648
649    set n 0
650    set table ""
651    set lines {}
652    lappend lines [list Title \
653			    [mc "Sessions"] \
654			    [mc "MAC address"] \
655			    [mc "Equipment"] \
656			    [mc "Interface"] \
657			    [mc "Vlan"] \
658			    [mc "Last occurrence"] \
659			]
660    set sql "SELECT DISTINCT 	src AS src,
661    				(data).mac AS mac,
662				(data).port AS port,
663				(data).vlanid AS vlanid
664		    FROM	mac.portmac
665		    WHERE	$crit
666		    GROUP BY    src, data"
667    set d {}
668    pg_select $mdbfd $sql tab {
669	incr n
670	set portmac_assoc_link [gen-link [mc "Details"] \
671		    "portmac_assoc:$tab(src),$tab(mac),$tab(port),$tab(vlanid)"]
672	set maclink [gen-link [display-mac $mdbfd $tab(mac)] $tab(mac)]
673
674    	if {$tab(vlanid) eq ""} then {
675	    set tab(vlanid) 0
676	}
677	set vlanid $tab(vlanid)
678	set vlanlink [gen-link [display-vlan $ndbfd $vlanid] "vlan:$vlanid"]
679	set eqname [get-eq-name $ndbfd $tab(src)]
680	set eqlink [gen-link [display-eq $eqname] "eq:$tab(src)"]
681
682	set sql "SELECT extract(epoch from max(stop)) AS stop
683		    FROM mac.portmac
684		    WHERE src='$tab(src)'
685			    AND (data).mac='$tab(mac)'
686			    AND (data).port='$tab(port)'
687			    AND (data).vlanid=$tab(vlanid)"
688	set stop ""
689	pg_select $mdbfd $sql tab2 {
690	    set stop $tab2(stop)
691	}
692
693	if {$stop ne ""} then {
694	    set stop [expr int($stop)]
695	} else {
696	    set stop 0
697	}
698	lappend d [list Data \
699	    $portmac_assoc_link $maclink $eqlink $tab(port) $vlanlink $stop ]
700
701    }
702
703    #
704    # Sort list and convert date
705    #
706
707    foreach l [lsort -index end -integer $d] {
708	set t [lindex $l end]
709	set ndate [clock format $t -format "$conf(clockformat)"]
710	if {[regexp {^01/01/1970} $ndate]} then {
711	    set ndate [mc "(no date)"]
712	}
713	lappend lines [lreplace $l end end $ndate]
714    }
715
716    if {$n > 0} then {
717	set table [mc "%s MAC-Port-VLAN associations found" $n]
718	append table [::arrgen::output "html" $conf(tabmenuportmac) $lines]
719    } else {
720	set table [mc "No MAC-Port-VLAN association found"]
721    }
722
723    return $table
724}
725
726#
727# Display an HTML link
728#
729
730proc gen-link {text target} {
731    global conf
732
733    d urlset "" $conf(next) [list [list "recherche" $target]]
734    set url [d urlget ""]
735    return [::webapp::helem "a" $text "href" $url]
736}
737
738#
739# Display a Vlan name: "vlanid (description)" or "vlanid"
740#
741
742proc display-vlan {ndbfd vlanid} {
743    global cachevlan
744
745    if {[info exists cachevlan($vlanid)]} then {
746	set descr $cachevlan($vlanid)
747    } else {
748	set descr ""
749	if {[regexp {^[0-9]+$} $vlanid]} then {
750	    set sql "SELECT descr AS descr FROM topo.vlan WHERE vlanid=$vlanid"
751	    pg_select $ndbfd $sql tab {
752		set descr $tab(descr)
753	    }
754	} else {
755	    set descr "-"
756	}
757	set cachevlan($vlanid) $descr
758    }
759
760    if {$descr ne ""} then {
761	set dispvlan "$vlanid ($descr)"
762    } else {
763	set dispvlan $vlanid
764    }
765
766    return $dispvlan
767}
768
769proc display-eq {eq} {
770    global conf
771
772    regsub "\.$conf(defaultdomain)\$" $eq "" eq
773
774    return $eq
775}
776
777#
778# Search equipment name given its address
779#
780# Input:
781#   - ndbfd : netmagis database handle
782#   - addr : ip address of equipment
783#
784# Return: equipment name or empty string if not found
785#
786
787proc get-eq-name {ndbfd addr} {
788    global cacheeq
789
790    if {[info exists cacheeq(addr:$addr)]} then {
791	set eq $cacheeq(addr:$addr)
792    } else {
793	set eq [mc "Unknown equipment (address %s)" $addr]
794	set sql "SELECT rr.name || '.' || domain.name AS fqdn
795		    FROM dns.rr_ip, dns.rr, dns.domain
796		    WHERE rr_ip.addr = '$addr'
797		    	AND rr.idrr = rr_ip.idrr
798			AND rr.iddom = domain.iddom"
799	pg_select $ndbfd $sql tab {
800	    set eq $tab(fqdn)
801	}
802	set cacheeq(name:$eq) $addr
803	set cacheeq(addr:$addr) $eq
804    }
805
806    return $eq
807}
808
809#
810# Search equipment addr given an equipment name
811#
812# Return: equipment IP address or empty string if not found
813#
814
815proc get-eq-addr {ndbfd eq} {
816    global cacheeq
817
818    if {[info exists cacheeq(name:$eq)]} then {
819	set addr $cacheeq(name:$eq)
820    } else {
821	set l [split $eq "."]
822	set shortname [lindex $l 0]
823	set domain [join [lrange $l 1 end] "."]
824	set sql "SELECT rr_ip.addr
825		    FROM dns.rr_ip, dns.rr, dns.domain
826		    WHERE rr.idrr = rr_ip.idrr
827		    	AND rr.iddom = domain.iddom
828		    	AND rr.name = '$shortname'
829		    	AND domain.name = '$domain'"
830	set addr ""
831	pg_select $ndbfd $sql tab {
832	    set addr $tab(addr)
833	}
834	set cacheeq(addr:$addr) $eq
835	set cacheeq(name:$eq) $addr
836    }
837    return $addr
838}
839
840#
841# Display search criterion on result page as a (somewhat) readable text
842#
843
844proc display-crit {ndbfd type param} {
845    switch $type {
846	mac		{ set m "MAC address %s" }
847	ip        	{ set m "IP address %s" }
848	ipmac_assoc	{ set m "IP-MAC" }
849	portmac_assoc	{ set m "MAC-Port-Vlan" }
850	vlan		{ set m "Vlan %s" ; set param [display-vlan $ndbfd $param] }
851	name		{ set m "host %s" }
852	default		{ set m "???" }
853    }
854    return [mc $m $param]
855}
856
857#
858# Format an IP address: either IP followed by DNS name if it exists, or IP
859# address only.
860#
861
862proc display-ip {ip dnsname} {
863    if {$dnsname ne ""} then {
864	set disp "$ip ($dnsname)"
865    } else {
866	set disp "$ip"
867    }
868    return $disp
869}
870
871#
872# Search OUI for a given MAC address and returns a string built from
873# MAC address and manufacturer name
874#
875# Example:
876# - MAC address "08:00:20:67:89:ab" becomes
877#	"08:00:20:67:89:ab (SUN MICROSYSTEMS INC.)"
878#
879# - MAC address mac "01:23:45:67:89:ab" becomes
880#	"01:23:45:67:89:ab"
881#	(no change since OUI is not found)
882#
883
884proc display-mac {mdbfd mac} {
885    global ouicache
886
887    if {[info exists ouicache($mac)]} then {
888	return $ouicache($mac)
889    }
890
891    set manuf 0
892    pg_select $mdbfd "SELECT mac.manuf('$mac') AS m" tab {
893	set manuf $tab(m)
894    }
895
896    if {$manuf ne ""} then {
897	set dispmac "$mac ($manuf)"
898    } else {
899	set dispmac "$mac"
900    }
901    set ouicache($mac) $dispmac
902    return $dispmac
903}
904
905#
906# DNS resolver : calls "host" command and produces an IP address list or a FQDN
907#
908# Input:
909#  - type : "name" for a standard resolution, "ip" for a reverse resolution
910#  - arg  : resolve parameter
911# Output:
912#  - return value: addresse list or domain name
913#
914
915proc resolve {type arg _msg} {
916    global conf
917    global dnscache
918    upvar $_msg msg
919
920    set msg ""
921
922    if {! ([regexp {^[a-z0-9\-.]+$} $arg] || [regexp {^([0-9.]+|[0-9a-f:.]+)$} $arg])} then {
923	return {}
924    }
925
926    #
927    # Filter definition
928    #
929
930    if {[info exists dnscache($arg)]} then {
931	return $dnscache($arg)
932    }
933
934    switch $type {
935	"name" { set pat "address" }
936	"ip"  { set pat "domain name pointer" }
937    }
938
939    #
940    # Extract "host" command result
941    #
942
943    set hostcmd [get-local-conf "hostcmd"]
944    if {[catch {exec $hostcmd $arg} buf]} then {
945	if {[regexp {Host .+ not found} $buf]} then {
946	    # the host was not found, not an error
947	} else {
948	    # real error
949	    set msg $buf
950	}
951        set buf ""
952    }
953
954    #
955    # Keep only the last word of each line
956    #
957
958    set l {}
959    foreach line [split $buf "\n"] {
960	if {[regexp $pat $line]} then {
961	    lappend l [lindex $line end]
962	}
963    }
964    set dnscache($arg) $l
965    return $l
966}
967
968##############################################################################
969# Display page
970##############################################################################
971
972d cgi-register {recherche {}} {} {
973    global conf
974
975    #
976    # End of script: output page and close database
977    #
978
979    d urlset "%URLFORM%" $conf(next) {}
980    d result $conf(pagesearch) {}
981}
982
983##############################################################################
984# Search
985##############################################################################
986
987d cgi-register {recherche .+} {} {
988    global conf
989
990    #
991    # Save netmagis database handler
992    #
993
994    set ndbfd $dbfd
995
996
997    #
998    # Get default domain
999    #
1000
1001    set conf(defaultdomain) [dnsconfig get "defdomain"]
1002
1003    #
1004    # Open MAC database
1005    #
1006
1007    set conninfo [get-conninfo "macdb"]
1008    if {[catch {set mdbfd [pg_connect -conninfo $conninfo]} msg]} then {
1009        d error $msg
1010    }
1011
1012    #
1013    # Guess search type
1014    #
1015
1016    set recherche [string tolower $recherche]
1017    set l [guess-type $mdbfd $ndbfd $recherche msg]
1018    if {[llength $l] == 0} then {
1019	d error $msg
1020    }
1021    lassign $l type param
1022
1023    #
1024    # Display output
1025    #
1026
1027    set table [html-result $mdbfd $ndbfd $type $param]
1028    if {$table eq ""} then {
1029	set table [mc "No information found"]
1030    }
1031
1032    #
1033    # End of script: output page and close database
1034    #
1035
1036    d result $conf(page) \
1037	[list \
1038		[list %RECHERCHE% [display-crit $mdbfd $type $param]] \
1039		[list %TABLEAU%   $table] \
1040	    ]
1041}
1042
1043##############################################################################
1044# Main procedure
1045##############################################################################
1046
1047d cgi-dispatch "mac" "mac"
1048