From da11d73276785f8923d60addc791f7b6e5e0cf2b Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Thu, 1 Oct 2009 12:17:03 -0600 Subject: [PATCH] Mostly cosmetic changes --- Makefile | 5 ++++- config.py | 17 ++++++++++++--- ctf.css | 17 ++++++++++----- flagd.py | 10 +++++++-- game.py | 8 ++++--- grunge.png | Bin 0 -> 5893 bytes histogram.py | 6 +++--- intro.html | 58 +++++++++++++++++++++++++++++++++++++++++++------- points.py | 29 +++++++++---------------- run.ctfd | 2 +- scoreboard.py | 40 ++++++++++++++++++++++++++-------- teams.py | 25 +++++++++++++++++----- 12 files changed, 158 insertions(+), 59 deletions(-) create mode 100644 grunge.png diff --git a/Makefile b/Makefile index 084fd70..1957826 100644 --- a/Makefile +++ b/Makefile @@ -22,13 +22,16 @@ target: $(PYC) $(INSTALL) ctfd.py $(CTFDIR) $(INSTALL) -d $(WWWDIR) - $(INSTALL) index.html intro.html ctf.css $(WWWDIR) + $(INSTALL) index.html intro.html ctf.css grunge.png $(WWWDIR) $(FAKE) ln -s /var/lib/ctf/histogram.png $(WWWDIR) $(INSTALL) register.cgi scoreboard.cgi puzzler.cgi $(WWWDIR) $(INSTALL) -d $(DESTDIR)/var/service/ctf $(INSTALL) run.ctfd $(DESTDIR)/var/service/ctf/run + $(INSTALL) -d $(DESTDIR)/var/service/ctf/log + $(INSTALL) run.log.ctfd $(DESTDIR)/var/service/ctf/log/run + rm -rf $(WWWDIR)/puzzler $(INSTALL) -d $(WWWDIR)/puzzler ./mkpuzzles.py --htmldir=$(WWWDIR)/puzzler --keyfile=$(CTFDIR)/puzzler.keys diff --git a/config.py b/config.py index e7bbeda..3e6fd44 100755 --- a/config.py +++ b/config.py @@ -2,17 +2,25 @@ import os +team_colors = ['F0888A', '88BDF0', '00782B', '999900', 'EF9C00', + 'F4B5B7', 'E2EFFB', '89CA9D', 'FAF519', 'FFE7BB', + 'BA88F0', '8DCFF4', 'BEDFC4', 'FFFAB2', 'D7D7D7', + 'C5B9D7', '006189', '8DCB41', 'FFCC00', '898989'] + if 'home' in os.environ.get('SCRIPT_FILENAME', ''): # We're a CGI running out of someone's home directory config = {'global': {'data_dir': '.', 'base_url': '.', 'css_url': 'ctf.css', - 'diasbled_dir': 'disabled' + 'diasbled_dir': 'disabled', + 'flags_dir': 'flags', + 'house_team': 'dirtbags', + 'passwd': 'passwd', + 'team_colors': team_colors, }, 'puzzler': {'dir': 'puzzles', - 'ignore_dir': 'puzzler.ignore', 'cgi_url': 'puzzler.cgi', 'base_url': 'puzzler', 'keys_file': 'puzzler.keys', @@ -25,10 +33,13 @@ else: 'base_url': '/', 'css_url': '/ctf.css', 'disabled_dir': '/var/lib/ctf/disabled', + 'flags_dir': '/var/lib/ctf/flags', + 'house_team': 'dirtbags', + 'passwd': '/var/lib/ctf/passwd', + 'team_colors': team_colors, }, 'puzzler': {'dir': '/usr/lib/www/puzzler', - 'ignore_dir': '/var/lib/ctf/puzzler.ignore', 'cgi_url': '/puzzler.cgi', 'base_url': '/puzzler', 'keys_file': '/usr/lib/ctf/puzzler.keys', diff --git a/ctf.css b/ctf.css index fa04941..37bd7fe 100644 --- a/ctf.css +++ b/ctf.css @@ -1,7 +1,7 @@ /**** document ****/ html { - background: #222 url(http://dirtbags.net/images/grunge.png) no-repeat; + background: #222 url(grunge.png) repeat-x; } body { @@ -29,18 +29,24 @@ a:hover { } code, pre, .readme { - color: #fff; - background-color: #555; - margin: 1em; + color: #fff; + background-color: #555; + margin: 1em; } +th, td { + vertical-align: top; +} + +.scoreboard td { + height: 400px; +} /**** heading ****/ h1:first-child { text-transform: lowercase; font-size: 1.6em; - letter-spacing: -0.1em; background-color: #222; opacity: 0.9; padding: 3px; @@ -50,6 +56,7 @@ h1:first-child { h1:first-child:before { color: #fff; + letter-spacing: -0.1em; content: "Capture The Flag: "; } diff --git a/flagd.py b/flagd.py index 862afc7..2d1362a 100755 --- a/flagd.py +++ b/flagd.py @@ -7,15 +7,19 @@ import functools import time import hmac import optparse +import os import points import pointscli import teams +import config import traceback key = b'My First Shared Secret (tm)' def hexdigest(data): return hmac.new(key, data).hexdigest() +flags_dir = config.get('global', 'flags_dir') + class Submitter(asyncore.dispatcher): def __init__(self, host='localhost', port=6667): asyncore.dispatcher.__init__(self) @@ -110,8 +114,10 @@ class FlagServer(asynchat.async_chat): def set_flag(self, team): self.flag = team - if self.cat: - self.submitter.set_flag(self.cat, team) + self.submitter.set_flag(self.cat, team) + f = open(os.path.join(flags_dir, self.cat), 'w') + if team: + f.write(team) def found_terminator(self): data = b''.join(self.inbuf) diff --git a/game.py b/game.py index 5104226..9dcd251 100755 --- a/game.py +++ b/game.py @@ -59,9 +59,11 @@ class Flagger(asynchat.async_chat): asynchat.async_chat.handle_error(self) def set_flag(self, team): - if not team: - team = 'dirtbags' - self.push(team.encode('utf-8') + b'\n') + if team: + eteam = team.encode('utf-8') + else: + eteam = b'' + self.push(eteam + b'\n') self.flag = team diff --git a/grunge.png b/grunge.png new file mode 100644 index 0000000000000000000000000000000000000000..2b98730b0354a8305ea9eb33e64d65ab7de8a393 GIT binary patch literal 5893 zcmV+g7y9UlP)bBnN00006P)t-s00RL3 z|Nr87b z9s6*82w2C3WPFj;0$(nHK;p*)!g+!NehL}mK*m^Bmm*`C;A9-~5W_9eLf-Br&O^Wt z=F~bF%McSC2?Rsn)UuM5A;jC0byhu5t1G`>RrSA_mMmG;bf40Nz1td%xuM9A}!!&sfYtOgdH@WAAzRn1xl3*JC=R!gG zOW=H};3xBy_79D*4c*b&DR@vQe3xWBjI!Ig%Wr@U~z8t#4#i7nL5!u&GZPFp<6K^6@YFG=#%I88kgVGd&h%I0HmCS8zB$> zq3dEGtSLYoYc4^+C2iY{0sk&S<3|#Zb6o)%n@5-7gJh2^=zM9p(?OZ6sRU5h9R=8n z0f`p-#Svr`?o%FW>i5xoaL3W%+4lJXQHKo~4lAp~()j#Rr;Qg`Xu(C7|C0RQu4y`%pIyX@n45gpZBrJ^ zLcp2;D79;d5S~Sf_$&{Ef|>%1M;xs9^{9wdi5hG&M~13(Ym zt455CpW*BL^AN6IhgS%rTUgqDiI64&LaHV-N;EW#%7ur3!7N%CzG)u(w>j|b3#6I8 za6B2BR06dlkcTrG0MpTCRbB4>3HYTqKKnN2D0RsWZ zBp~;4O#Ihp0`!Q*d)psaj5-o|o`C`uWI01JOu_$6k^N>y0Gnh+q<7zr z6$BrIZ!B9NU!H>NX9D#9@Rj2m+pnAEx8QVq)&C1{H8|Mbc7JovoitdTSS%))%rR_J z{oN(kBS1&_Ae^yH*aCK85A81jDEiyLc3ryGyJNk~p(XAG%h0%JeDFeV^TD3i)R{q4 zJspOlEc>-DIznzirdKB30TXu!vxAp7fB)vr`2i0VAfem6w{CUKmk{vV1a0jb-``tt zy{5))QBH21q+WVp{}1Q0?{TSMpzjJm5-z}3_q1MKnmz?k#|9k>q7Aui?IxLMJ#ibS zL*Ri97{^%Iqi$zjV+)!B%np{vXsCY1m4FZ=ApkDp%i)u~^CfENgajZX_NUNK66#t^ z6MgV^HSTVN25TFx5B{}4mPKp~jgF9PXz1ySNPE2KL8jmQrV=77G=rlkaGiqG7~wxFhQFlmrnZ$UK5^z49Of=vWu-k zd2$q*X9RRO|JYLc8vVFY)`DdlLWUAg!~`}~KaUCuv=ad{a;5-bLT$-X;Qbx~cIlsm zXH}3v%(X0i$OD8z)4HlqA$e^B;VRdz5PcC|)1& z!eDt6Fa*C>b4;g>3Qm3pQ{Ei5b^thEpN;)(KJKs(f1ai0Mwv)&j{xp-L%5NTHJzJp zZbCcvC(&|BYJ6yKCV)Lr4WaUKav%@y8VHENT9dt}=1!;Q8S}ADOY$o%U%}a-AUQ_o zPXJ%wYb#jUb9~V8wlaW_o*=pVkE(S2r$&$SY_Yo4%oV94AvN~JK1eOdw8#7dF!A4V z4};{8>$t=@kqfw*0IrF3kg)X&tf2t6wKYDJKc*)$?NqikI9oxn_=j-zhXA<7JzX>n z8vJGr%^nDCRi`gndFJa0pe_JEZOZ9C0`}+%cTTy~+ zYf3;W8rcU}xdh;j$v?3Xa3)r8!1obwW|3IT+&x?mY;zDo3+-e9@eBV1m4?Se{>wJ^ zE!VD*$EAroDZn>PH4)zia0>w^1>jvG*8dy?gcH=wZLb$BTm63~=7CT!o!(3ukh4|Y z+DU1~1tWg|QtHR99yW;`GWcEeT|T&D%Vh#+qqr*oXAF7Q0OBRwXLg9?U~bCX@{C~N zMO+th5ztBl&QqUUXxPvPyY@Yzg)pJPl_SVI(1YkZd;p$@SQ1c2z=i;Hh*&HoJeYbK zG@LRwT|Jnj-X1ZSjb})E`NC=HoMm81aP5bL28Q2$Cd8>2dmI3__+UfSGRW3z#xVj0 zY6=5@ph}HG&-^NzCsWpnr-NVgw-_a;FBGwwSIg8l{R{HmDWwiV^uA*s0^lF{ZA~yh zo3Zk*o4mg+0I?r4xI+^_7XfQE&0DqxAI?+KybcOJ^KX5}gmvo@?7 z11=OKK7XT4LjhRZcqiP9OpJiwo|*tuQ5=3sUWe}7_P_Zdcwy^tNAveN0K&Tt94u+L zNJ)MXQ{Uo6O>7PJxd-k}Ixt(>6o9xaIxiHA8htWww6uajO(cbHdZOkp(17|cz{{2c z8C{eSQ?2$j$c4v(wSQj#&OPZbGENpDAi$jdGslB?v`vJBH_C%o98b6*7?X<1^g1-J{A0l1RE{RuSZ(u=#t3UCVmHL>mzD=54^YB&x4 zW1>Z!mYZoyzp#n%#ITqM2Ltdb$KVUkVU& zU3F`=G4AOu`_&gbS*i{g@OG5ze^E*u%cL5ZdiM*I~QtS0!b|9cI#%~7> zuH@-3zv!m{wEz_S6aa%5&?4Fv50^;X2k6lKyQlQa^n!pwK?|1}-w~Mv0DnY01xU!R zlm2fF0aAF|axzcTeacT`(9RAxG8e_UZCf6If}U_Q0NkucfHlY|-uE(qTo%2^%XDV= z#CA?u<g#QCoECJBp`;PQZP0##tmkSEMOhq7A@y$~HY*Ma5Sp zZuPBbmzpTN*Rz1;5TNE?8lbTe2n9|45mkVgs2ly1i3OlH0+% zGSW9PfRib}S0!LZwD8gZh4+JkHu|7kt3n^7dnq3jQIG(39dVQaeD9L@D7&vNWXmWf zk%D`VMS!(oz}BKT%4FYLmj-ySF#;&k%;stYctcK5Bfz_)xhT@5dxn*Ziv!ZgQ~-s5 zkq=V37Dcg<1#~g%P(HNWt6i24UgxoinHI{Ow_DgEXsajq;9ao@9&hcuY(R^rd{68Z)1?loOf6Cn(JaoL@z~ft z^wd2kN5?2W3N1*~q)DN8q$-JAb5ja3;TmjAfHL8NsTnR2&_llQKwkW&0(>k6)TM&h z-f3e4v=YD?0zTjinv75r@6QBL1`%4K&1c0vQ*C@EwqroF9er>-t;vl!Cf-jB;ykDT z{di^39Xnvnf(!Qp1e_8ty_WVt)em~{+N?8nAJl9CxFFYi;U$_h3$cm4_~*t2xbMj+ ziK@kJFrh_&D{^yPCW^h}{y+hMxQY=>#|o~AL|w}O`eO&&b2EVMk}lWU>KfPidMj4Y zm4K?ZpR%|&Za^XcKk+q9bOKEc_p`NP1sw$_XCgQ4@hRxUfLM#S0ty_5xV1hs))A1# z3R1b$pn_^lfXj@;fX8sK`8JX3y)7#=EE^h`Yq5efQP3D$npyssq!lD?3jsl$nT2It zl$3f#!GOf?A5*Cd5C z8Tm=v2%Ml`k$^aBx8hz;0q!P%;xRL$NE0880LsU<)x9^J6r8AWJEeS>XF>u#E_Q5X z6*R}z!{h+sQ*0tgYBETg+yTIM#W!nZ01QYviW!*!z#Roh>aUs72MYxxJnMtRy3f__ zXPp%eh|8-Iu$BNuCWh{>3?^3hvouYa7#E_GrvsFNdQo}C^drE!O4Rt-AH16Z1O+vx zz$i|pl%mw(6l7RIzfi!XOsT^MK01+;kB8mc74 z0SW~j)z}Ch#GTv1#Pzrq8^tv{R*ZpHC}1}O z*hawe1c@KSPd7@y8L<>RoMyc1g#j)tW)+-ij{-tBQ&_v@ciT}vNR$uaj*t-r8wK%^ zevo{IgamZ-(I6=fkZuNXM@_}eMALl?6zmQwSXehh{Sm-dK*3IF0CD4cHqD5&qq$N6 z&Sn6QM*)q30oxfsT)%v`G2Cty2e?Gqoe{uRW2BoYP(jvCngzA({JpvIDAt zs9>Rhvl&2RS1Oo%_70?(TES^1<%40o&uFm}dGOhj0h2_%USJ+fw7DVEOfU*q*PJ>j zei)I0L$|XDscB9L_*5mKfysyey#`$`1)!=VffODnL?J1^=Fv5-?e}Rv#WcQCbB_$pFLrC@vLX z*2HRkxM0A=VZdM*uvQKQLt89;*rO&c7_glNB%#gylKJ3VRzZt@oS6xfCrCnZQkJ3tMFd1RpFhplDe2J0%4qo;0y>r%_VCj;bVYmsP$6%c(L&x(+!JwaN!86IXy$9<17}%EVPI zt`w{)NoC?n!3wYftS0XNkNd&O2df~dYVl7ypk8(p>y?QwsvxNVuR37aeb8W)f)!u| zSVeJV;>yHVx)wiHb_Gj69wIxgb00000NkvXXu0mjf^hJvx literal 0 HcmV?d00001 diff --git a/histogram.py b/histogram.py index 434ac68..65309d0 100755 --- a/histogram.py +++ b/histogram.py @@ -4,6 +4,7 @@ import points import time import os import tempfile +import teams import config pngout = config.datafile('histogram.png') @@ -16,8 +17,7 @@ def main(s=None): s = points.Storage() plotparts = [] - teams = s.teams() - teamcolors = points.colors(teams) + teams = s.teams catscores = {} for cat in s.categories(): @@ -27,7 +27,7 @@ def main(s=None): fn = scoresfile.name i = 2 for team in teams: - plotparts.append('"%s" using 1:%d with lines linewidth 2 linetype rgb "#%s"' % (fn, i, teamcolors[team])) + plotparts.append('"%s" using 1:%d with lines linewidth 2 linetype rgb "#%s"' % (fn, i, teams.color(team))) scores[team] = 0 i += 1 diff --git a/intro.html b/intro.html index 0eea942..f1220cb 100644 --- a/intro.html +++ b/intro.html @@ -10,7 +10,7 @@

Introduction

- Welcome to Capture The Flag, blah blah blah. + Welcome to Capture The Flag.

What This Is

@@ -35,7 +35,9 @@

Important Rules

  • The contest network is 10.x.x.x. Do - not attack machines outside the contest network.
  • + not attack machines outside the contest network. All + federal, state, and school laws still apply to the outside + network.
  • If the "outside network" requires you to plug into a different switch, do not connect any machine that has been on the contest network.
  • @@ -52,10 +54,49 @@
  • If IRC is up, you should use it to communicate with the contest staff. Staff will have operator status in #ctf.
  • If you think something is wrong with the game, you are - expected to demonstrate the problem and explain why you think - it's a problem.
  • + expected to demonstrate the problem and explain what you think + is the correct behavior.
+

Scoring

+ +

+ The contest is made up of multiple categories. Each + category is worth one point toward the total score; the percentage + of the total points held by your team is the percentage of one + point your team has for that category. The team that has 30% of + the points in each of five categories has 1.5 points, whereas the + team that has 80% of the points in only one category has 0.8 + points. +

+ +

+ There are two kinds of categories: flags, + and puzzles. +

+ +

Flags

+ +

+ Flag categories are challenges with a notion of a winner + or service availability. In these categories, the + flag-holder (the winner, or each team with a running service) + makes 1 point per minute for as long as they hold the flag. If + there is a single flag-holder, and the flag changes hands, a point + is awarded to the new winner at the moment the flag moves. +

+ +

Puzzles

+ +

+ Most of the categories come in the form of + multiple puzzles: for each puzzle presented, a key + (answer) must be found to recieve the amount of points that puzzle + is worth. Any team may answer any puzzle question at any time. A + new puzzle is revealed when a team correctly answers the + highest-valued puzzle in that category. +

+

Hints

@@ -63,15 +104,16 @@ points, though. For puzzles, you will lose ΒΌ of the points for that puzzle even if you never solve the puzzle. For other events, the staff member will decide how many points it will - cost. You can try to bribe us or otherwise fanagle information - out of us, or other contestants. It's a hacking contest. + cost. You can try to bribe or otherwise fanagle information out + of us or other contestants. It's a hacking contest.

About Us

- We are the dirtbags. You might - be better at this than we are, but you're not running the contest. + We are the dirtbags. People + pay us money to do the sorts of things you'll be doing in this + contest.

diff --git a/points.py b/points.py index b818691..2f419c9 100755 --- a/points.py +++ b/points.py @@ -93,7 +93,7 @@ class Storage: def __init__(self, fn=None): if not fn: fn = config.datafile('scores.dat') - self.points_by_team = {} + self.teams = set() self.points_by_cat = {} self.points_by_cat_team = {} self.log = [] @@ -128,7 +128,7 @@ class Storage: def add(self, req, write=True): when, cat, team, score = req - incdict(self.points_by_team, team, score) + self.teams.add(team) incdict(self.points_by_cat, cat, score) incdict(self.points_by_cat_team, (cat, team), score) self.log.append(req) @@ -146,31 +146,23 @@ class Storage: def categories(self): return sorted(self.points_by_cat) - def teams(self): - return sorted(self.points_by_team) + def get_teams(self): + return sorted(self.teams) def cat_points(self, cat): return self.points_by_cat.get(cat, 0) def team_points(self, team): - return self.points_by_team.get(team, 0) + points = 0 + for cat, tot in self.points_by_cat.items(): + team_points = self.team_points_in_cat(cat, team) + points += team_points / float(tot) + return points def team_points_in_cat(self, cat, team): return self.points_by_cat_team.get((cat, team), 0) -## -## Colors -## -def colors(teams): - colors = ['F0888A', '88BDF0', '00782B', '999900', 'EF9C00', - 'F4B5B7', 'E2EFFB', '89CA9D', 'FAF519', 'FFE7BB', - 'BA88F0', '8DCFF4', 'BEDFC4', 'FFFAB2', 'D7D7D7', - 'C5B9D7', '006189', '8DCB41', 'FFCC00', '898989'] - return dict(zip(teams, colors)) - - - ## ## Testing @@ -198,9 +190,8 @@ def test(): s.add((now, 'cat1', 'zebras', 20)) s.add((now, 'cat1', 'aardvarks', 10)) s.add((now, 'merf', 'aardvarks', 50)) - assert s.teams() == ['aardvarks', 'zebras'] + assert s.get_teams() == ['aardvarks', 'zebras'] assert s.categories() == ['cat1', 'merf'] - assert s.team_points('aardvarks') == 60 assert s.cat_points('cat1') == 30 assert s.team_points_in_cat('cat1', 'aardvarks') == 10 assert s.team_points_in_cat('merf', 'zebras') == 0 diff --git a/run.ctfd b/run.ctfd index fa143a3..c55f5b3 100755 --- a/run.ctfd +++ b/run.ctfd @@ -1,4 +1,4 @@ #! /bin/sh -/usr/lib/ctf/ctfd.py 2>&1 | logger -t ctfd +exec /usr/lib/ctf/ctfd.py diff --git a/scoreboard.py b/scoreboard.py index e6936d4..f09684f 100755 --- a/scoreboard.py +++ b/scoreboard.py @@ -1,42 +1,64 @@ #!/usr/bin/env python3 import cgitb; cgitb.enable() +import os import config +import teams import points +flags_dir = config.get('global', 'flags_dir') +house_team = config.get('global', 'house_team') + def main(): s = points.Storage() - teams = s.teams() categories = [(cat, s.cat_points(cat)) for cat in s.categories()] - teamcolors = points.colors(teams) print('Content-type: text/html') print() - print(''' - CTF Scoreboard + Scoreboard

Scoreboard

''' % config.base_url) - print('') + print('
') print('') + print('') for cat, score in categories: - print('' % (cat, score)) + print('') print('') print('') + print('') for cat, total in categories: - print('
Overall%s (%d)') + print(' %s (%d)' % (cat, score)) + try: + fn = os.path.join(flags_dir, cat) + team = open(fn).read() or house_team + print('
') + print(' flag: %s' + % (cat, teams.color(team), team)) + except IOError: + pass + print('
    ') + totals = [] + for team in s.teams: + total = s.team_points(team) + totals.append((total, team)) + for total, team in sorted(totals, reverse=True): + print('
  1. %s (%0.3f)
  2. ' + % (teams.color(team), team, total)) + print('
') - scores = sorted([(s.team_points_in_cat(cat, team), team) for team in teams]) + print('') + scores = sorted([(s.team_points_in_cat(cat, team), team) for team in s.teams]) for score, team in scores: - color = teamcolors[team] + color = teams.color(team) print('
' % (float(score * 100)/total, color)) print(' %s: %d' % (cat, team, score)) print('
') diff --git a/teams.py b/teams.py index a7be5de..4cba5ca 100755 --- a/teams.py +++ b/teams.py @@ -2,12 +2,13 @@ import fcntl import time +import config import os from urllib.parse import quote, unquote -house = 'dirtbags' - -passwdfn = '/var/lib/ctf/passwd' +house = config.get('global', 'house_team') +passwdfn = config.get('global', 'passwd') +team_colors = config.get('global', 'team_colors') teams = {} built = 0 @@ -25,7 +26,9 @@ def build_teams(): for line in f: line = line.strip() team, passwd = [unquote(v) for v in line.strip().split('\t')] - teams[team] = passwd + color = team_colors.pop(0) + team_colors.append(color) + teams[team] = (passwd, color) except IOError: pass built = time.time() @@ -35,7 +38,7 @@ def validate(team): def chkpasswd(team, passwd): validate(team) - if teams.get(team) == passwd: + if teams.get(team, [None, None])[0] == passwd: return True else: return False @@ -51,3 +54,15 @@ def add(team, passwd): fcntl.lockf(f, fcntl.LOCK_EX) f.seek(0, 2) f.write('%s\t%s\n' % (quote(team), quote(passwd))) + +def color(team): + t = teams.get(team) + if not t: + validate(team) + t = teams.get(team) + if not t: + return '888888' + return t[1] + + +