Small sh "framework" to test some server responses
sh
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

check.sh 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. #!/bin/sh
  2. #check.sh : functionnal check functions for various server
  3. #Copyright (C) 2016 Weber Yann
  4. #
  5. #This program is free software; you can redistribute it and/or modify
  6. #it under the terms of the GNU General Public License as published by
  7. #the Free Software Foundation; either version 3 of the License, or
  8. #any later version.
  9. #
  10. #This program is distributed in the hope that it will be useful,
  11. #but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. #GNU General Public License for more details.
  14. #
  15. #You should have received a copy of the GNU General Public License
  16. #along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. if [ -z "$color" ]
  18. then
  19. color=1
  20. fi
  21. if [ -z "$verbose" ]
  22. then
  23. verbose=1
  24. fi
  25. if [ -z "$exit_on_fail" ]
  26. then
  27. exit_on_fail=1
  28. fi
  29. alias echo="/bin/echo -e"
  30. col_reset() {
  31. if [ "$color" -gt 0 ]
  32. then
  33. tput sgr0
  34. fi
  35. }
  36. col_set() {
  37. if [ -z "$1" ]
  38. then
  39. return
  40. fi
  41. if [ "$color" -gt 0 ]
  42. then
  43. tput setaf "$1"
  44. fi
  45. }
  46. log() {
  47. echo "$(col_set "$3")[$(printf "%7s" "$1")]$(col_reset) $2"
  48. }
  49. datetime() {
  50. date -Iseconds
  51. }
  52. logdate() {
  53. echo "$(date -Iseconds) $(col_set "$3")[$(printf "%7s" "$1")]$(col_reset) $2"
  54. }
  55. fail() {
  56. if [ "$verbose" -lt 2 ]
  57. then
  58. echo "" #for the dot printed with -n
  59. fi
  60. test_msg="$1"
  61. test_status=1
  62. }
  63. err() {
  64. msg="$(log ERR "$1" 1)"
  65. test_msg="$1"
  66. test_status=-1
  67. }
  68. success() {
  69. msg="$(log OK "$1" 2)"
  70. test_msg="$1"
  71. test_status=0
  72. }
  73. #
  74. # Tests & testcase functions
  75. #
  76. tc_name=""
  77. tc_infos=""
  78. tc_fail=0
  79. tc_run=0
  80. total_fail=0
  81. total_run=0
  82. test_msg=""
  83. test_status=0
  84. _test_setup() {
  85. test_status=0
  86. test_msg=""
  87. }
  88. TC_INIT() {
  89. tc_name=$1
  90. tc_infos=$2
  91. tc_fail=0
  92. tc_run=0
  93. msg="Running testcase '$tc_name'"
  94. if [ -n "$tc_infos" ]
  95. then
  96. msg="${msg} ($tc_infos)"
  97. fi
  98. if [ "$verbose" -gt 0 ]
  99. then
  100. logdate TESTCASE "$msg" 4
  101. fi
  102. }
  103. TC_END() {
  104. if [ "$tc_fail" -gt 0 ]
  105. then
  106. if [ "$verbose" -gt 1 ]
  107. then
  108. logdate TESTCASE "-------------$tc_name END------------" 1
  109. fi
  110. logdate FAIL "Testcase '$tc_name' tests: ${tc_run} fails: $(col_set 1)${tc_fail}$(col_reset)" 1
  111. if [ "$exit_on_fail" -ne 0 ]
  112. then
  113. CHECK_REPORT
  114. fi
  115. elif [ "$verbose" -eq 1 ]
  116. then
  117. echo "" #for the dot printed with -n
  118. logdate TESTCASE "Testcase '$tc_name' tests:${tc_run} fails: $tc_fail" 2
  119. elif [ "$verbose" -gt 1 ]
  120. then
  121. logdate TESTCASE "-------------$tc_name END------------" 4
  122. fi
  123. tc_name=""
  124. tc_infos=""
  125. tc_fail=0
  126. tc_run=0
  127. }
  128. TC_RUN() {
  129. _test_setup
  130. cmd=$1
  131. shift
  132. case $#
  133. in
  134. 1)$cmd "$1";;
  135. 2)$cmd "$1" "$2";;
  136. 3)$cmd "$1" "$2" "$3";;
  137. 4)$cmd "$1" "$2" "$3" "$4";;
  138. *)fail "To many arguments for $cmd"
  139. esac
  140. tc_run=$(( tc_run + 1))
  141. total_run=$(( total_run + 1))
  142. if [ "$test_status" -ne 0 ]
  143. then
  144. err_type="FAIL"
  145. if [ "$test_status" -lt 0 ]
  146. then
  147. err_type="ERR"
  148. fi
  149. tc_fail=$(( tc_fail + 1))
  150. total_fail=$(( total_fail + 1))
  151. logdate $err_type "$tc_name: $test_msg" 1 >&2
  152. elif [ "$verbose" -gt 1 ]
  153. then
  154. logdate OK "$tc_name: $test_msg" 2
  155. else
  156. printf '.'
  157. fi
  158. }
  159. CHECK_START() {
  160. logdate STATUS "Starting tests" 3
  161. }
  162. CHECK_REPORT() {
  163. if [ "$verbose" -eq 0 ] && [ "$tc_fail" -eq "0" ]
  164. then
  165. echo "" # for dot printed with -n
  166. fi
  167. if [ "$verbose" -gt 0 ]
  168. then
  169. logdate STATUS "All tests done" 3
  170. fi
  171. if [ "$total_fail" -gt 0 ]
  172. then
  173. logdate FAIL "Summary : tests: ${total_run} fails: $(col_set 1)${total_fail}$(col_reset)" 1
  174. exit 1
  175. else
  176. logdate OK "Summary : tests:${total_run} fails: $total_fail" 2
  177. exit 0
  178. fi
  179. }
  180. #
  181. # HTTP/HTTPS tests
  182. #
  183. _check_http_status() {
  184. # $1 url
  185. # $2 status
  186. # $* curl options
  187. url=$1
  188. shift 1
  189. expt=$1
  190. if [ -z "$1" ]
  191. then
  192. expt=200
  193. fi
  194. shift 1
  195. status=$(curl -s -o /dev/null -w "%{http_code}" "$@" "$url")
  196. if [ "$status" -ne "$expt" ]
  197. then
  198. fail "Check http status $expt for $url : $status returned"
  199. elif [ "$verbose" -gt 0 ]
  200. then
  201. success "Check http status $status for $url"
  202. fi
  203. }
  204. check_http_status() {
  205. # $1 url
  206. # $2 status
  207. _check_http_status "$1" "$2"
  208. }
  209. check_http_200() {
  210. _check_http_status "$1" 200
  211. }
  212. check_https_cert() {
  213. # Check that SSL Cert is valid
  214. # $1 URL
  215. status=$(curl -s -o /dev/null -w "%{http_code}" "https://$1")
  216. rep=$?
  217. if [ "$rep" -eq 0 ]
  218. then
  219. success "https://$1 cert verified"
  220. return
  221. fi
  222. status=$(curl -k -s -o /dev/null -q "%{http_code}" "https://$1")
  223. rep=$?
  224. if [ "$rep" -eq 0 ]
  225. then
  226. fail "https://$1 cert invalid"
  227. else
  228. err "Unable to curl https://$1"
  229. fi
  230. }
  231. check_html_title() {
  232. url="$1"
  233. expt="$2"
  234. tmpxsl=$(mktemp -t XSL.XXXXXXXXXXX)
  235. echo '<?xml version="1.0"?>
  236. <xsl:stylesheet version="1.0"
  237. xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  238. <xsl:output method = "text"/>
  239. <xsl:template match="/">
  240. <xsl:value-of select="/html/head/title"/>
  241. </xsl:template>
  242. </xsl:stylesheet>' > "$tmpxsl"
  243. tmphtml=$(mktemp -t html.XXXXXXXXX)
  244. curl --silent "$url" > "$tmphtml"
  245. if title=$(xsltproc --html --novalid "$tmpxsl" "$tmphtml" 2>/dev/null)
  246. then
  247. :
  248. else
  249. title=$(xsltproc --novalid "$tmpxsl" "$tmphtml" 2>/dev/null)
  250. fi
  251. if [ "$title" = "$expt" ]
  252. then
  253. success "$url HTML title is '$expt'"
  254. else
  255. fail "$url HTML title is '$title' but '$expt' expected"
  256. fi
  257. rm "$tmpxsl" "$tmphtml" 2>/dev/null
  258. }
  259. check_audiostream() {
  260. # Uses one of mplayer or mpv to retrieve a small amount of stream
  261. # and attempt to decode it.
  262. # $1 Stream URL
  263. MPLAYER="$(which mplayer)"
  264. MPV="$(which mpv)"
  265. if [ -x "$MPLAYER" ]
  266. then
  267. check_audiostream_mplayer "$1"
  268. elif [ -x "$MPV" ]
  269. then
  270. check_audiostream_mpv "$1"
  271. else
  272. fail "Unable to find mplayer nor mpv"
  273. fi
  274. }
  275. check_audiostream_mplayer() {
  276. # Uses mplayer to fetch 128Kb (by default) of stream
  277. # $1 Stream URL
  278. # $2 size in kb without the kb suffix
  279. # $3 mplayer path
  280. tmpfile=$(mktemp -t check_audiostream.XXXXXXXXX)
  281. sz="$2"
  282. if [ -z "$sz" ]
  283. then
  284. sz="128"
  285. fi
  286. MPLAYER="$3"
  287. if [ -z "$MPLAYER" ]
  288. then
  289. MPLAYER="$(which mplayer)"
  290. fi
  291. sz_kb="${sz}kb"
  292. expt_sz="$(( sz * 8 ))"
  293. if [ "$verbose" -gt 1 ]
  294. then
  295. logdate INFO "$tc_name: Running mplayer on '$1' for $sz_kb" 3
  296. fi
  297. $MPLAYER -endpos "$sz_kb" -ao "pcm:file=$tmpfile" "$1" 1>/dev/null 2>/dev/null
  298. res="$?"
  299. bytes=$(du -b "$tmpfile" | cut -f1)
  300. rm "$tmpfile" 2>/dev/null
  301. if [ "$bytes" -lt $expt_sz ]
  302. then
  303. fail "mplayer retrieved ${bytes}B of stream instead of expected $sz_kb"
  304. return
  305. fi
  306. if [ "$res" -eq 0 ]
  307. then
  308. success "mplayer retrieved $sz of stream on $1"
  309. else
  310. fail "mplayer failed to retrieve stream on $1"
  311. fi
  312. }
  313. check_audiostream_mpv() {
  314. # Uses mpv to fetch 4s of audio stream
  315. # $1 Stream URL
  316. # $2 time of stream to fetch
  317. # $3 mpv path
  318. time="$2"
  319. if [ -z "$time" ]
  320. then
  321. time="4"
  322. fi
  323. MPV="$3"
  324. if [ -z "$MPV" ]
  325. then
  326. MPV="$(which mpv)"
  327. fi
  328. if [ "$verbose" -gt 1 ]
  329. then
  330. logdate INFO "$tc_name: Running mpv on '$1' for ${time}s" 3
  331. fi
  332. start_time=$(date "+%s")
  333. $MPV --vo=null --ao=null --o=/dev/null --of=wav --length $time "$1" 1> /dev/null 2>/dev/null
  334. res=$?
  335. stop_time=$(date "+%s")
  336. run_time=$(( stop_time - start_time))
  337. if [ "$run_time" -lt "$time" ]
  338. then
  339. fail "mpv stopped running ${run_time} after launch but ${time}s should be received"
  340. return
  341. fi
  342. if [ "$res" -eq 0 ]
  343. then
  344. success "mpv retrieved ${time}s of stream on $1"
  345. else
  346. fail "mpv failed to retrieve ${time}s of stream on $1"
  347. fi
  348. }
  349. check_ping() {
  350. hostname=$1
  351. count=$2
  352. proto="$3"
  353. if [ -z "$count" ]
  354. then
  355. count=5
  356. fi
  357. case "$proto" in
  358. ipv4)
  359. protoping="ping -4";;
  360. ipv6)
  361. protoping="ping -6";;
  362. *)
  363. protoping="ping";;
  364. esac
  365. if $protoping -i 0.2 -c "$count" "$hostname" >/dev/null
  366. then
  367. success "successfully send $count ping to '$hostname'"
  368. else
  369. fail "unable to ping '$hostname'"
  370. fi
  371. }
  372. check_dns() {
  373. hostname=$1
  374. field=$2
  375. expt=$3
  376. result=$(dig +short "$hostname" "$field")
  377. if echo "$result" | grep -e "$expt" >/dev/null
  378. then
  379. success "DNS replied '$expt' for '$field $hostname'"
  380. else
  381. fail "Unexpected DNS reply '$result' expecting '$expt' for '$hostname $field'"
  382. fi
  383. }
  384. check_smtp() {
  385. hostname=$1
  386. port=$2
  387. if [ -z "$port" ]
  388. then
  389. port=587
  390. fi
  391. code=$(nc -q0 "$hostname" "$port" < /dev/null | cut -d " " -f1)
  392. if [ "$code" = "220" ]
  393. then
  394. success "SMTP@$hostname server replied 220 status"
  395. else
  396. fail "Unexpected reply from smtp@$hostname '$code'"
  397. fi
  398. }
  399. check_smtp_id() {
  400. hostname=$1
  401. idstr=$2
  402. port=$3
  403. if [ -z "$port" ]
  404. then
  405. port=587
  406. fi
  407. rep=$(nc -q0 "$hostname" "$port" < /dev/null)
  408. if echo "$rep" | grep -e "^220 $idstr" > /dev/null
  409. then
  410. success "SMTP@$hostname server replie '$idstr'"
  411. else
  412. fail "Unexpected reply from smtp@$hostname '$rep'"
  413. fi
  414. }
  415. check_imaps() {
  416. hostname=$1
  417. port=$2
  418. if [ -z "$port" ]
  419. then
  420. port=993
  421. fi
  422. rep=$(echo "01 LOGOUT" | openssl s_client -quiet -verify_quiet -connect "${hostname}:$port" -servername "$hostname")
  423. if echo "$rep" | grep "^01 OK" > /dev/null
  424. then
  425. success "IMAPS@$hostname '$(echo "$rep" | grep "^01 OK")'"
  426. else
  427. fail "Unexpected reply from imaps@$hostname '$rep'"
  428. fi
  429. }
  430. check_git_repo() {
  431. tmpdir=$(mktemp -d -t check_git.XXXXXXXXX)
  432. git clone "$1" "$tmpdir" 2>/dev/null 1>/dev/null
  433. res="$?"
  434. rm -Rf "$tmpdir"
  435. if [ "$res" -ne 0 ]
  436. then
  437. fail "unable to clone git repo '$1'"
  438. else
  439. success "git repo '$1' cloned'"
  440. fi
  441. }
  442. #
  443. # Jabber XMPP checks
  444. #
  445. __xmpp_probe() {
  446. serv=$1
  447. timeout=$2
  448. type=$3
  449. payload=$(printf "%s" "<?xml version=\"1.0\"?>\n<stream:stream xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" xmlns=\"jabber:$type\" to=\"${1}\" xml:lang=\"en\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\">\n")
  450. if [ "$verbose" -gt 2 ]
  451. then
  452. echo "$payload" | sed -e "s/^/$(datetime) [ DEBUG] Sent : /" >&2
  453. fi
  454. echo "$payload"
  455. sleep "$timeout"
  456. }
  457. _xmpp_probe() {
  458. serv=$1
  459. port=$2
  460. timeout=$3
  461. type=$4
  462. tmpres=$(mktemp -t xmpp_probe.XXXXXXXXX)
  463. echo "$(__xmpp_probe "$serv" "$timeout" "$type"| nc -q2 "$serv" "$port")</stream:stream>" | xmllint --format - > "$tmpres"
  464. if [ "$verbose" -gt 2 ]
  465. then
  466. sed -e "s/^/$(datetime) [ DEBUG] Recv : /" >&2 < "$tmpres"
  467. fi
  468. cat "$tmpres"
  469. rm "$tmpres"
  470. }
  471. _check_xmpp_ns() {
  472. ns1=$1
  473. expt_ns=$2
  474. expt_port=$3
  475. dnsq="_xmpp-${4}._tcp.${ns1}"
  476. rep="$(dig "$dnsq" srv +short | cut -d" " -f3,4)"
  477. if [ "$rep" = "$expt_port ${expt_ns}." ]
  478. then
  479. success "$dnsq = '$rep'"
  480. else
  481. fail "$dnsq = '$rep' but '$expt_port ${expt_ns}.' expected"
  482. fi
  483. }
  484. check_xmpp_serv_ns() {
  485. _check_xmpp_ns "$1" "$2" "$3" "server"
  486. }
  487. check_xmpp_client_ns() {
  488. _check_xmpp_ns "$1" "$2" "$3" "client"
  489. }
  490. _check_xmpp_server() {
  491. serv=$1
  492. port=$2
  493. timeout=$3
  494. type=$4
  495. if [ -z "$port" ]
  496. then
  497. port=5222
  498. fi
  499. if [ -z "$timeout" ]
  500. then
  501. timeout=2
  502. fi
  503. if [ "$verbose" -gt 1 ]
  504. then
  505. tpe="client"
  506. if [ "$type" = "server" ]
  507. then
  508. tpe="S2S"
  509. fi
  510. logdate INFO "$tc_name: Connecting to XMPP $serv $tpe port $port (timeout=${timeout}s)" 3
  511. fi
  512. stream=$(_xmpp_probe "$serv" "$port" "$timeout" "$type"| head -n2 | tail -n1)
  513. if [ -z "$stream" ]
  514. then
  515. fail "Empty reply from $serv:$port"
  516. return
  517. fi
  518. if [ "$type" = "client" ]
  519. then
  520. infos=$(echo "$stream" | sed -E 's/^<([^ ]+).* xmlns="([^"]+)".* from="([^"]+)" .*$/\1 \2 \3/')
  521. else
  522. infos="$(echo "$stream" | sed -E 's/^<([^ ]+).* xmlns="([^"]+)" .*$/\1 \2/') $serv"
  523. fi
  524. if echo "$infos" | grep "jabber:$type" >/dev/null
  525. then
  526. success "Successfully connected to XMPP $type $serv:$port"
  527. else
  528. fail "Unexpected reply from $serv:$port : $infos"
  529. fi
  530. }
  531. check_xmpp_server_client() {
  532. _check_xmpp_server "$1" "$2" "$3" "client"
  533. }
  534. check_xmpp_server_s2s() {
  535. _check_xmpp_server "$1" "$2" "$3" "server"
  536. }
  537. check_xmpp_ssl() {
  538. serv=$1
  539. port=$2
  540. if [ -z "$port" ]
  541. then
  542. port=5222
  543. fi
  544. openssl s_client -connect "$serv:$port" </dev/null -starttls xmpp >/dev/null 2>/dev/null
  545. rep=$?
  546. if [ "$rep" -eq 0 ]
  547. then
  548. success "Openssl successfully negociating XMPP ssl with $serv:$port"
  549. else
  550. fail "Openssl failed negociating XMPP ssl with $serv:$port"
  551. fi
  552. }
  553. #
  554. # SSH checks
  555. #
  556. check_ssh_nc() {
  557. host=$1
  558. port=$2
  559. if [ -z "$port" ]
  560. then
  561. port=22
  562. fi
  563. rep="$(nc -w1 "$host" "$port" </dev/null)"
  564. res=$?
  565. if [ "$res" -ne "0" ]
  566. then
  567. fail "Netcat unable to connect to $host:$port"
  568. return
  569. fi
  570. if echo "$rep" | grep "^SSH-2.0-OpenSSH" >/dev/null
  571. then
  572. success "OpenSSH replied on $host:$port"
  573. else
  574. fail "Bad reply from $host:$port : '$rep'"
  575. fi
  576. }
  577. check_ssh_key() {
  578. host="$1"
  579. testkey="$2"
  580. keytype="$3"
  581. port="$4"
  582. if [ -z "$port" ]
  583. then
  584. port=22
  585. fi
  586. if [ -z "$keytype" ]
  587. then
  588. keytype="rsa"
  589. fi
  590. key=$(ssh-keyscan -p $port -t "$keytype" "$host" 2>/dev/null | cut -d " " -f3)
  591. if [ -z "$key" ]
  592. then
  593. fail "SSH server not responding"
  594. return
  595. elif [ "$key" = "$testkey" ]
  596. then
  597. success "OpenSSH $host:$port key is $testkey"
  598. return
  599. else
  600. fail "OpenSSH $host:$port missmatch : "
  601. logdate ERR "Expected : $testkey" 1
  602. logdate ERR "Received : $key" 1
  603. return
  604. fi
  605. }
  606. check_mpc() {
  607. # check_mpc [$host [$port] ]
  608. # tests if you can contact an MPD server in a client way
  609. # returns the current state of the server
  610. host=$1
  611. port=$2
  612. if [ -z "$host" ]
  613. then
  614. host=127.0.0.1
  615. fi
  616. if [ -z "$port" ]
  617. then
  618. port=6600
  619. fi
  620. if echo "status" | nc -w 1 "$host" "$port" | head -1 | grep "^OK MPD"
  621. then
  622. success "MPD server can be contacted on $host:$port"
  623. status=$(echo "status" | nc -w 1 "$host" "$port" | grep "^state:"|cut -d: -f2)
  624. success "It's state is : $status"
  625. fi
  626. }